// // 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 }
// // 数据: 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 }