//
// Client <---> Proxy[BackendConn] <---> RPC Server[包含LB]
// BackConn <====> RPC Server
// loopReader从RPC Server读取数据,然后根据返回的结果来设置: Client的Request的状态
//
// 1. bc.flushRequest
// 2. bc.setResponse
//
func (bc *BackendConn) loopReader(c *TBufferedFramedTransport, connOver *sync.WaitGroup) {
	go func() {
		defer connOver.Done()
		defer c.Close()

		lastTime := time.Now().Unix()
		// Active状态,或者最近5s有数据返回
		// 设计理由:服务在线,则请求正常发送;
		//         服务下线后,则期待后端服务的数据继续返回(最多等待5s)
		for bc.IsConnActive.Get() || (time.Now().Unix()-lastTime < 5) {
			// 读取来自后端服务的数据,通过 setResponse 转交给 前端
			// client <---> proxy <-----> backend_conn <---> rpc_server
			// ReadFrame需要有一个度? 如果碰到EOF该如何处理呢?

			// io.EOF在两种情况下会出现
			//
			resp, err := c.ReadFrame()
			lastTime = time.Now().Unix()
			if err != nil {
				err1, ok := err.(thrift.TTransportException)
				if !ok || err1.TypeId() != thrift.END_OF_FILE {
					log.ErrorErrorf(err, Red("[%s]ReadFrame From Server with Error: %v"), bc.service, err)
				}
				bc.flushRequests(err)
				break
			} else {

				bc.setResponse(nil, resp, err)
			}
		}

		bc.flushRequests(errors.New("BackendConn Timeout"))
	}()
}
//
// 目前有两类请求:
// 1. ping request
// 2. 正常的请求
func (bc *BackendConn) PushBack(r *Request) {
	if bc.IsConnActive.Get() && !bc.IsMarkOffline.Get() {
		// 1. 处于Active状态,并且没有标记下线, 则将 Request 添加到 input 中
		r.Wait.Add(1)
		bc.input <- r
	} else {
		// 2. 直接报错(返回)
		r.Response.Err = errors.New(fmt.Sprintf("[%s] Request Assigned to inactive BackendConn", bc.service))
		log.Warn(Magenta("Push Request To Inactive Backend"))
	}
}
Example #3
0
//
// 将Request分配给BackendConnLB
//
func (bc *BackendConnLB) PushBack(r *Request) {
	// 关键路径必须有Log, 高频路径的Log需要受verbose状态的控制
	if bc.IsConnActive.Get() {
		r.Service = bc.serviceName
		r.Wait.Add(1)
		bc.input <- r

	} else {
		r.Response.Err = errors.New(fmt.Sprintf("[%s] Request Assigned to inactive BackendConnLB", bc.serviceName))
		log.Warn(Magenta("Push Request To Inactive Backend"))
	}

}
Example #4
0
const (
	B = 1 << (10 * iota)
	KB
	MB
	GB
	TB
	PB
)

var (
	BytesizeRegexp = regexp.MustCompile(`(?i)^\s*(\-?[\d\.]+)\s*([KMGTP]?B|[BKMGTP]|)\s*$`)
	digitsRegexp   = regexp.MustCompile(`^\-?\d+$`)
)

var (
	ErrBadBytesize     = errors.New("invalid byte size")
	ErrBadBytesizeUnit = errors.New("invalid byte size unit")
)

func Parse(s string) (int64, error) {
	if !BytesizeRegexp.MatchString(s) {
		return 0, errors.Trace(ErrBadBytesize)
	}

	subs := BytesizeRegexp.FindStringSubmatch(s)
	if len(subs) != 3 {
		return 0, errors.Trace(ErrBadBytesize)
	}

	size := int64(0)
	switch strings.ToUpper(string(subs[2])) {
Example #5
0
func NewRollingFile(basePath string, logKeepDays int) (io.WriteCloser, error) {
	if logKeepDays <= 0 {
		return nil, errors.Errorf("invalid max file-frag = %d", logKeepDays)
	}
	if _, file := path.Split(basePath); file == "" {
		return nil, errors.Errorf("invalid base-path = %s, file name is required", basePath)
	}

	return &rollingFile{
		logKeepDays:     logKeepDays,
		basePath:        basePath,
		nextStartOfDate: nextStartOfDay(time.Now()),
	}, nil
}

var ErrClosedRollingFile = errors.New("rolling file is closed")

func (r *rollingFile) roll() error {
	if r.file != nil {
		// 还没有日期切换
		now := time.Now()
		if now.Before(r.nextStartOfDate) {
			return nil
		}
		r.file.Close()
		r.file = nil
		r.nextStartOfDate = nextStartOfDay(now)

		// 将logKeepDays之前几天的日志删除
		for i := r.logKeepDays; i < r.logKeepDays+4; i++ {
//
// 将 bc.input 中的Request写入后端的服务器
//
func (bc *BackendConn) loopWriter(c *TBufferedFramedTransport) error {

	defer func() {
		// 关闭心跳的Ticker
		bc.hbTicker.Stop()
		bc.hbTicker = nil
	}()

	var r *Request
	var ok bool

	// 准备HB Ticker
	bc.hbTicker = time.NewTicker(time.Second)
	bc.hbLastTime.Set(time.Now().Unix())

	for true {
		// 等待输入的Event, 或者 heartbeatTimeout
		select {
		case <-bc.hbTicker.C:
			if time.Now().Unix()-bc.hbLastTime.Get() > HB_TIMEOUT {
				return errors.New(fmt.Sprintf("[%s]HB timeout", bc.service))
			} else {
				// 定时添加Ping的任务; 如果标记下线,则不在心跳
				if !bc.IsMarkOffline.Get() {
					// 发送心跳信息
					r := NewPingRequest()
					bc.PushBack(r)

					// 同时检测当前的异常请求
					expired := microseconds() - REQUEST_EXPIRED_TIME_MICRO // 以microsecond为单位
					// microseconds() - request.Start > REQUEST_EXPIRED_TIME_MICRO
					// 超时: microseconds() - REQUEST_EXPIRED_TIME_MICRO > request.Start
					bc.seqNumRequestMap.RemoveExpired(expired)

				}
			}

		case r, ok = <-bc.input:
			if !ok {
				return nil
			} else {
				//
				// 如果暂时没有数据输入,则p策略可能就有问题了
				// 只有写入数据,才有可能产生flush; 如果是最后一个数据必须自己flush, 否则就可能无限期等待
				//
				if r.Request.TypeId == MESSAGE_TYPE_HEART_BEAT {
					// 过期的HB信号,直接放弃
					if time.Now().Unix()-r.Start > 4 {
						log.Printf(Magenta("Expired HB Signal"))
					}
				}

				// 请求正常转发给后端的Rpc Server
				var flush = len(bc.input) == 0

				// 1. 替换新的SeqId
				r.ReplaceSeqId(bc.currentSeqId)
				bc.IncreaseCurrentSeqId()

				// 2. 主动控制Buffer的flush
				// 先记录SeqId <--> Request, 再发送请求
				// 否则: 请求从后端返回,记录还没有完成,就容易导致Request丢失
				bc.seqNumRequestMap.Add(r.Response.SeqId, r)

				// 2. 主动控制Buffer的flush
				c.Write(r.Request.Data)
				err := c.FlushBuffer(flush)

				if err == nil {
					log.Debugf("--> SeqId: %d vs. %d To Backend", r.Request.SeqId, r.Response.SeqId)

				} else {
					bc.seqNumRequestMap.Pop(r.Response.SeqId) // 如果写错了,在删除
					// 进入不可用状态(不可用状态下,通过自我心跳进入可用状态)
					return bc.setResponse(r, nil, err)
				}
			}
		}
	}

	return nil
}
Example #7
0
//
// 数据: LB ---> backend services
//
// 如果input关闭,且loopWriter正常处理完毕之后,返回nil
// 其他情况返回error
//
func (bc *BackendConnLB) loopWriter() error {
	// 正常情况下, ok总是为True; 除非bc.input的发送者主动关闭了channel, 表示再也没有新的Task过来了
	// 参考: https://tour.golang.org/concurrency/4
	// 如果input没有关闭,则会block
	c := NewTBufferedFramedTransport(bc.transport, 100*time.Microsecond, 20)

	// bc.MarkConnActiveOK() // 准备接受数据
	// BackendConnLB 在构造之初就有打开的transport, 并且Active默认为OK

	defer func() {
		bc.hbTicker.Stop()
		bc.hbTicker = nil
	}()

	bc.loopReader(c) // 异步

	// 建立连接之后,就启动HB
	bc.hbTicker = time.NewTicker(time.Second)
	bc.hbLastTime.Set(time.Now().Unix())

	var r *Request
	var ok bool

	for true {
		// 等待输入的Event, 或者 heartbeatTimeout
		select {
		case <-bc.hbTicker.C:
			// 两种情况下,心跳会超时
			// 1. 对方挂了
			// 2. 自己快要挂了,然后就不再发送心跳;没有了心跳,就会超时
			if time.Now().Unix()-bc.hbLastTime.Get() > HB_TIMEOUT {
				// 强制关闭c
				c.Close()
				return errors.New("Worker HB timeout")
			} else {
				if bc.IsConnActive.Get() {
					// 定时添加Ping的任务
					r := NewPingRequest()
					bc.PushBack(r)

					// 同时检测当前的异常请求
					expired := microseconds() - REQUEST_EXPIRED_TIME_MICRO // 以microsecond为单位
					bc.seqNumRequestMap.RemoveExpired(expired)
				}
			}

		case r, ok = <-bc.input:
			if !ok {
				return nil
			} else {
				// 如果暂时没有数据输入,则p策略可能就有问题了
				// 只有写入数据,才有可能产生flush; 如果是最后一个数据必须自己flush, 否则就可能无限期等待
				//
				if r.Request.TypeId == MESSAGE_TYPE_HEART_BEAT {
					// 过期的HB信号,直接放弃
					if time.Now().Unix()-r.Start > 4 {
						log.Warnf(Red("Expired HB Signals"))
					}
				}
				// 先不做优化
				var flush = true // len(bc.input) == 0

				// 1. 替换新的SeqId(currentSeqId只在当前线程中使用, 不需要同步)
				r.ReplaceSeqId(bc.currentSeqId)
				bc.IncreaseCurrentSeqId()

				// 2. 主动控制Buffer的flush
				// 先记录SeqId <--> Request, 再发送请求
				// 否则: 请求从后端返回,记录还没有完成,就容易导致Request丢失
				bc.seqNumRequestMap.Add(r.Response.SeqId, r)
				c.Write(r.Request.Data)
				err := c.FlushBuffer(flush)

				if err != nil {
					bc.seqNumRequestMap.Pop(r.Response.SeqId)
					log.ErrorErrorf(err, "FlushBuffer Error: %v\n", err)

					// 进入不可用状态(不可用状态下,通过自我心跳进入可用状态)
					return bc.setResponse(r, nil, err)
				}
			}
		}

	}
	return nil
}