Example #1
0
//
// evtch:
// 1. 来自Zookeeper驱动通知
// 2. 该通知最终需要通过 evtbus 传递给其他人
//
func (top *Topology) doWatch(evtch <-chan topo.Event, evtbus chan interface{}) {
	e := <-evtch

	// http://wiki.apache.org/hadoop/ZooKeeper/FAQ
	// 如何处理? 照理说不会发生的
	if e.State == topo.StateExpired || e.Type == topo.EventNotWatching {
		log.Warnf("session expired: %+v", e)
		evtbus <- e
		return
	}

	log.Warnf("topo event %+v", e)

	switch e.Type {
	//case topo.EventNodeCreated:
	//case topo.EventNodeDataChanged:
	case topo.EventNodeChildrenChanged: //only care children changed
		//todo:get changed node and decode event
	default:

		// log.Warnf("%+v", e)
	}

	evtbus <- e
}
//
// 清除过期的Request
//
func (c *RequestMap) RemoveExpired(expiredInMicro int64) {
	c.lock.Lock()

	for true {
		ent := c.evictList.Back()

		// 如果Map为空,则返回
		if ent == nil {
			break
		}

		// 如果请求还没有过期,则不再返回
		entry := ent.Value.(*Entry)
		request := entry.value
		if request.Start > expiredInMicro {
			break
		}

		// 1. 准备删除当前的元素
		c.removeElement(ent)

		// 2. 如果出问题了,则打印原始的请求的数据
		log.Warnf(Red("Remove Expired Request: %s.%s [%d]"),
			request.Service, request.Request.Name, request.Response.SeqId)

		// 3. 处理Request
		request.Response.Err = request.NewTimeoutError()
		request.Wait.Done()
	}

	c.lock.Unlock()
}
// 读取Frame的完整的数据,包含
func (p *TBufferedFramedTransport) ReadFrame() (frame []byte, err error) {

	if p.FrameSize != 0 {
		err = thrift.NewTTransportExceptionFromError(
			fmt.Errorf("Unexpected frame size: %d", p.FrameSize))
		return nil, err
	}
	var frameSize int

	// Reader来自transport, 中间被封装了多长
	frameSize, err = p.readFrameHeader()
	if err != nil {
		err1, ok := err.(thrift.TTransportException)

		if ok {
			err = thrift.NewTTransportException(err1.TypeId(),
				fmt.Sprintf("Frame Header Read Error: %s", err1.Error()))
		} else {
			err = thrift.NewTTransportExceptionFromError(
				fmt.Errorf("Frame Header Read Error: %s", err.Error()))
		}
		return
	}

	bytes := getSlice(frameSize, frameSize)

	// 什么时候会出现?
	// 1. 如果tcp package比较大,则容易出现package的一部分先到,另一部分随后再到
	// 2. 在同一个机器上的两个进程之间出现的概率低(Time Delay小); 跨机器访问,出现概率高
	var l int
	l, err = io.ReadFull(p.Reader, bytes)
	//	l, err = p.Reader.Read(bytes)
	if l != frameSize {
		log.Warnf(Red("<==== ReadFrame frame size: %d, Got: %d"), frameSize, l)
	}

	if err != nil {
		err1, ok := err.(thrift.TTransportException)
		if ok {
			err = thrift.NewTTransportException(err1.TypeId(),
				fmt.Sprintf("Frame Data Read Error: %s", err1.Error()))
		} else {
			err = thrift.NewTTransportExceptionFromError(
				fmt.Errorf("Frame Data Read Error: %s", err.Error()))
		}
		return nil, err
	}

	return bytes, nil

}
Example #4
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
}