// // 确保Socket成功连接到后端服务器 // func (bc *BackendConn) ensureConn() (transport thrift.TTransport, err error) { // 1. 创建连接(只要IP没有问题, err一般就是空) timeout := time.Second * 5 if strings.Contains(bc.addr, ":") { transport, err = thrift.NewTSocketTimeout(bc.addr, timeout) } else { transport, err = rpc_utils.NewTUnixDomainTimeout(bc.addr, timeout) } log.Printf(Cyan("[%s]Create Socket To: %s"), bc.service, bc.addr) if err != nil { log.ErrorErrorf(err, "[%s]Create Socket Failed: %v, Addr: %s", err, bc.service, bc.addr) // 连接不上,失败 return nil, err } // 2. 只要服务存在,一般不会出现err sleepInterval := 1 err = transport.Open() for err != nil && !bc.IsMarkOffline.Get() { log.ErrorErrorf(err, "[%s]Socket Open Failed: %v, Addr: %s", bc.service, err, bc.addr) // Sleep: 1, 2, 4这几个间隔 time.Sleep(time.Duration(sleepInterval) * time.Second) if sleepInterval < 4 { sleepInterval *= 2 } err = transport.Open() } return transport, err }
// // 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")) }() }
// // 从RPC Backend中读取结果, ReadFrame读取的是一个thrift message // 存在两种情况: // 1. 正常读取thrift message, 然后从frame解码得到seqId, 然后得到request, 结束请求 // 2. 读取错误 // 将现有的requests全部flush回去 // func (bc *BackendConnLB) loopReader(c *TBufferedFramedTransport) { go func() { defer c.Close() for true { // 坚信: EOF只有在连接被关闭的情况下才会发生,其他情况下, Read等操作被会被block住 // EOF有两种情况: // 1. 连接正常关闭,最后数据等完整读取 --> io.EOF // 2. 连接异常关闭,数据不完整 --> io.ErrUnexpectedEOF // // rpc_server ---> backend_conn frame, err := c.ReadFrame() // 有可能被堵住 if err != nil { err1, ok := err.(thrift.TTransportException) if !ok || err1.TypeId() != thrift.END_OF_FILE { log.ErrorErrorf(err, Red("ReadFrame From rpc_server with Error: %v\n"), err) } bc.flushRequests(err) break } else { bc.setResponse(nil, frame, err) } } }() }
// 配对 Request, resp, err // PARAM: resp []byte 为一帧完整的thrift数据包 func (bc *BackendConnLB) setResponse(r *Request, data []byte, err error) error { // log.Printf("#setResponse: data: %v", data) // 表示出现错误了 if data == nil { log.Printf("No Data From Server, error: %v\n", err) r.Response.Err = err } else { // 从resp中读取基本的信息 typeId, method, seqId, err := DecodeThriftTypIdSeqId(data) // 解码错误,直接报错 if err != nil { log.ErrorErrorf(err, "Decode SeqId Error: %v", err) return err } if typeId == MESSAGE_TYPE_STOP { // 不再接受新的输入 // 直接来自后端的服务(不遵循: Request/Reply模型) bc.MarkConnActiveFalse() return nil } // 找到对应的Request req := bc.seqNumRequestMap.Pop(seqId) // 如果是心跳,则OK if typeId == MESSAGE_TYPE_HEART_BEAT { bc.hbLastTime.Set(time.Now().Unix()) return nil } if req == nil { log.Errorf("#setResponse not found, seqId: %d", seqId) return nil } else { if req.Response.SeqId != seqId { log.Errorf("Data From Server, SeqId not match, Ex: %d, Ret: %d", req.Request.SeqId, seqId) } r = req r.Response.TypeId = typeId if req.Request.Name != method { data = nil err = req.NewInvalidResponseError(method, "conn_lb") } } } r.Response.Data, r.Response.Err = data, err // 还原SeqId if data != nil { r.RestoreSeqId() } r.Wait.Done() return err }
// Router负责监听zk中服务列表的变化 func (bk *Router) WatchServices() { var evtbus chan interface{} = make(chan interface{}, 2) // 1. 保证Service目录存在,否则会报错 servicesPath := bk.topo.ProductServicesPath() _, e1 := bk.topo.CreateDir(servicesPath) if e1 != nil { log.PanicErrorf(e1, "Zk Path Create Failed: %s", servicesPath) } go func() { for true { // 无限监听 services, err := bk.topo.WatchChildren(servicesPath, evtbus) if err == nil { bk.serviceLock.Lock() // 保证数据更新是有效的 oldServices := bk.services bk.services = make(map[string]*BackService, len(services)) for _, service := range services { log.Println("Found Service: ", Magenta(service)) back, ok := oldServices[service] if ok { bk.services[service] = back delete(oldServices, service) } else { bk.addBackService(service) } } if len(oldServices) > 0 { for _, conn := range oldServices { // 标记下线(现在应该不会有新的请求,最多只会处理一些收尾的工作 conn.Stop() } } bk.serviceLock.Unlock() // 等待事件 <-evtbus } else { log.ErrorErrorf(err, "zk watch error: %s, error: %v\n", servicesPath, err) time.Sleep(time.Duration(5) * time.Second) } } }() // 读取zk, 等待 log.Println("ProductName: ", Magenta(bk.topo.ProductName)) }
func (p *TBufferedFramedTransport) flushTransport(force bool) error { if p.nbuffered > 0 && (force || p.needFlush()) { // 这个如何控制呢? if err := p.Writer.Flush(); err != nil { log.ErrorErrorf(err, "FlushTransport Error, %v", err) return err } p.nbuffered = 0 p.lastflush = time.Now().UnixNano() } return nil }
// // 先写入数据,然后再Flush Transport // func (p *TBufferedFramedTransport) FlushBuffer(force bool) error { size := p.Buffer.Len() // 没有有效的数据,直接返回 if size == 0 { return nil } // TODO: 待优化 force = true // 1. 将p.buf的大小以BigEndian模式写入: buf中 buf := p.LenghW[:4] binary.BigEndian.PutUint32(buf, uint32(size)) // log.Printf("----> Frame Size: %d, %v\n", size, buf) // 然后transport中先写入: 长度信息 _, err := p.Writer.Write(buf) if err != nil { return thrift.NewTTransportExceptionFromError(err) } // 2. 然后继续写入p.buf中的数据 if size > 0 { var ( n int64 err error ) // 如果 err == io.ErrShortWrite, p.Writer中也有buffer, 因此可以不用考虑异常 if n, err = p.Buffer.WriteTo(p.Writer); err != nil { log.ErrorErrorf(err, "Error Flushing Expect Write: %d, but %d\n", size, n) return thrift.NewTTransportExceptionFromError(err) } if n < int64(size) { log.Printf(Red("Buffer Write Not Finished")) } } p.nbuffered++ // Buffer重新开始处理数据 p.Buffer.Reset() // Flush Buffer return p.flushTransport(force) }
// // 不断建立到后端的逻辑,负责: BackendConn#input到redis的数据的输入和返回 // func (bc *BackendConn) Run() { for k := 0; !bc.IsMarkOffline.Get(); k++ { // 1. 首先BackendConn将当前 input中的数据写到后端服务中 transport, err := bc.ensureConn() if err != nil { log.ErrorErrorf(err, "[%s]BackendConn#ensureConn error: %v", bc.service, err) return } connOver := &sync.WaitGroup{} c := NewTBufferedFramedTransport(transport, 100*time.Microsecond, 20) bc.MarkConnActiveOK() // 准备接受数据 connOver.Add(1) bc.loopReader(c, connOver) // 异步(读取来自后端服务器的返回数据) // 2. 将 bc.input 中的请求写入 后端的Rpc Server err = bc.loopWriter(c) // 同步 // 3. 停止接受Request bc.MarkConnActiveFalse() // 等待Conn正式关闭 connOver.Wait() // 4. 将bc.input中剩余的 Request直接出错处理 if err == nil { log.Printf(Red("[%s]BackendConn#loopWriter normal Exit..."), bc.service) break } else { // 对于尚未处理的Request, 直接报错 for i := len(bc.input); i != 0; i-- { r := <-bc.input bc.setResponse(r, nil, err) } } } }
// // // NonBlock和Block的区别: // NonBlock的 Request和Response是不需要配对的, Request和Response是独立的,例如: // ---> RequestA, RequestB // <--- RequestB, RequestA 后请求的,可以先返回 // func (s *NonBlockSession) loopWriter(tasks <-chan *Request) error { for r := range tasks { // 1. tasks中的请求是已经请求完毕的,loopWriter负责将它们的数据写回到rpc proxy s.handleResponse(r) // 2. 将结果写回给Client _, err := s.TBufferedFramedTransport.Write(r.Response.Data) r.Recycle() if err != nil { log.ErrorErrorf(err, "SeqId: %d, Write back Data Error: %v\n", r.Request.SeqId, err) return err } // typeId, sedId, _ := DecodeThriftTypIdSeqId(r.Response.Data) // if typeId != MESSAGE_TYPE_HEART_BEAT { // if sedId != s.lastSeqId { // log.Errorf(Red("Invalid SedId for Writer: %d vs. %d"), sedId, s.lastSeqId) // } // } // log.Printf(Magenta("Task: %d"), r.Response.SeqId) // 3. Flush err = s.TBufferedFramedTransport.FlushBuffer(len(tasks) == 0) // len(tasks) == 0 // if err != nil { // log.Debugf(Magenta("Write Back to Client/Proxy SeqId: %d, Error: %v"), r.Request.SeqId, err) // } else { // log.Debugf(Magenta("Write Back to Client/Proxy SeqId: %d"), r.Request.SeqId) // } if err != nil { return err } } return nil }
// // go test proxy -v -run "TestBackend" // func TestBackend(t *testing.T) { // 作为一个Server transport, err := thrift.NewTServerSocket("127.0.0.1:0") assert.NoError(t, err) err = transport.Open() // 打开Transport assert.NoError(t, err) defer transport.Close() err = transport.Listen() // 开始监听 assert.NoError(t, err) addr := transport.Addr().String() fmt.Println("Addr: ", addr) var requestNum int32 = 10 requests := make([]*Request, 0, requestNum) var i int32 for i = 0; i < requestNum; i++ { buf := make([]byte, 100, 100) l := fakeData("Hello", thrift.CALL, i+1, buf[0:0]) buf = buf[0:l] req, _ := NewRequest(buf, false) req.Wait.Add(1) // 因为go routine可能还没有执行,代码就跑到最后面进行校验了 assert.Equal(t, i+1, req.Request.SeqId, "Request SeqId是否靠谱") requests = append(requests, req) } go func() { // 客户端代码 bc := NewBackendConn(addr, nil, "test", true) bc.currentSeqId = 10 // 准备发送数据 var i int32 for i = 0; i < requestNum; i++ { fmt.Println("Sending Request to Backend Conn", i) bc.PushBack(requests[i]) requests[i].Wait.Done() } // 需要等待数据返回? time.Sleep(time.Second * 2) }() go func() { // 服务器端代码 tran, err := transport.Accept() if err != nil { log.ErrorErrorf(err, "Error: %v\n", err) } assert.NoError(t, err) bt := NewTBufferedFramedTransport(tran, time.Microsecond*100, 2) // 在当前的这个t上读写数据 var i int32 for i = 0; i < requestNum; i++ { request, err := bt.ReadFrame() assert.NoError(t, err) req, _ := NewRequest(request, false) assert.Equal(t, req.Request.SeqId, i+10) fmt.Printf("Server Got Request, and SeqNum OK, Id: %d, Frame Size: %d\n", i, len(request)) // 回写数据 bt.Write(request) bt.FlushBuffer(true) } tran.Close() }() fmt.Println("Requests Len: ", len(requests)) for idx, r := range requests { r.Wait.Wait() // r 原始的请求 req, _ := NewRequest(r.Response.Data, false) log.Printf(Green("SeqMatch[%d]: Orig: %d, Return: %d\n"), idx, req.Request.SeqId, r.Request.SeqId) assert.Equal(t, req.Request.SeqId, r.Request.SeqId) } log.Println("OK") }
func (p *ThriftRpcServer) Run() { // // 1. 创建到zk的连接 p.Topo = NewTopology(p.ProductName, p.ZkAddr) // 127.0.0.1:5555 --> 127_0_0_1:5555 lbServiceName := GetServiceIdentity(p.FrontendAddr) exitSignal := make(chan os.Signal, 1) signal.Notify(exitSignal, syscall.SIGTERM, syscall.SIGINT, syscall.SIGKILL) // syscall.SIGKILL // kill -9 pid // kill -s SIGKILL pid 还是留给运维吧 StartTicker(p.config.FalconClient, p.ServiceName) // 初始状态为不上线 var state atomic2.Bool state.Set(false) stateChan := make(chan bool) // 注册服务 evtExit := make(chan interface{}) endpoint := RegisterService(p.ServiceName, p.FrontendAddr, lbServiceName, p.Topo, evtExit, p.config.WorkDir, p.config.CodeUrlVersion, &state, stateChan) // 3. 读取"前端"的配置 var transport thrift.TServerTransport var err error isUnixDomain := false // 127.0.0.1:9999(以:区分不同的类型) if !strings.Contains(p.FrontendAddr, ":") { if rpc_utils.FileExist(p.FrontendAddr) { os.Remove(p.FrontendAddr) } transport, err = rpc_utils.NewTServerUnixDomain(p.FrontendAddr) isUnixDomain = true } else { transport, err = thrift.NewTServerSocket(p.FrontendAddr) } if err != nil { log.ErrorErrorf(err, Red("Server Socket Create Failed: %v"), err) panic(fmt.Sprintf("Invalid FrontendAddr: %s", p.FrontendAddr)) } err = transport.Listen() if err != nil { log.ErrorErrorf(err, Red("Server Socket Open Failed: %v"), err) panic(fmt.Sprintf("Server Socket Open Failed: %s", p.FrontendAddr)) } ch := make(chan thrift.TTransport, 4096) defer close(ch) // 强制退出? TODO: Graceful退出 go func() { <-exitSignal evtExit <- true log.Info(Magenta("Receive Exit Signals....")) endpoint.DeleteServiceEndpoint(p.Topo) // 等待 start := time.Now().Unix() for true { // 如果5s内没有接受到新的请求了,则退出 now := time.Now().Unix() if now-p.lastRequestTime.Get() > 5 { log.Info(Red("Graceful Exit...")) break } else { log.Printf(Cyan("Sleeping %d seconds\n"), now-start) time.Sleep(time.Second) } } transport.Interrupt() transport.Close() }() go func() { var address string for c := range ch { // 为每个Connection建立一个Session socket, ok := c.(rpc_utils.SocketAddr) if ok { if isUnixDomain { address = p.FrontendAddr } else { address = socket.Addr().String() } } else { address = "unknow" } x := NewNonBlockSession(c, address, p.Verbose, &p.lastRequestTime) // Session独立处理自己的请求 go x.Serve(p, 1000) } }() // 准备上线服务 state.Set(true) stateChan <- true // Accept什么时候出错,出错之后如何处理呢? for { c, err := transport.Accept() if err != nil { break } else { ch <- c } } }
// // go test proxy -v -run "TestSession" // func TestSession(t *testing.T) { // 作为一个Server transport, err := thrift.NewTServerSocket("127.0.0.1:0") err = transport.Open() // 打开Transport defer transport.Close() err = transport.Listen() // 开始监听 assert.NoError(t, err) addr := transport.Addr().String() fmt.Println("Addr: ", addr) // 1. Fake Requests var requestNum int32 = 10 requests := make([]*Request, 0, requestNum) var i int32 for i = 0; i < requestNum; i++ { buf := make([]byte, 100, 100) l := fakeData("Hello", thrift.CALL, i+1, buf[0:0]) buf = buf[0:l] req, _ := NewRequest(buf, true) req.Wait.Add(1) // 因为go routine可能还没有执行,代码就跑到最后面进行校验了 assert.Equal(t, i+1, req.Request.SeqId, "Request SeqId是否靠谱") requests = append(requests, req) } // 2. 将请求交给BackendConn go func() { // 模拟请求: // 客户端代码 bc := NewBackendConn(addr, nil, "test", true) bc.currentSeqId = 10 // 上线 BackendConn bc.IsConnActive.Set(true) // 准备发送数据 var i int32 for i = 0; i < requestNum; i++ { fmt.Println("Sending Request to Backend Conn", i) bc.PushBack(requests[i]) requests[i].Wait.Done() } // 需要等待数据返回? time.Sleep(time.Second * 2) }() server := &fakeServer{} go func() { // 服务器端代码 tran, err := transport.Accept() defer tran.Close() if err != nil { log.ErrorErrorf(err, "Error: %v\n", err) } assert.NoError(t, err) // 建立一个长连接, 同上面的: NewBackendConn通信 session := NewSession(tran, "", true) session.Serve(server, 6) time.Sleep(time.Second * 2) }() for i = 0; i < requestNum; i++ { fmt.Println("===== Before Wait") requests[i].Wait.Wait() fmt.Println("===== Before After Wait") log.Printf("Request: %d, .....", i) assert.Equal(t, len(requests[i].Request.Data), len(requests[i].Response.Data)) } }
// // 去ZK注册当前的Service // func RegisterService(serviceName, frontendAddr, serviceId string, topo *Topology, evtExit chan interface{}, workDir string, codeUrlVerion string, state *atomic2.Bool, stateChan chan bool) *ServiceEndpoint { // 1. 准备数据 // 记录Service Endpoint的信息 servicePath := topo.ProductServicePath(serviceName) // 确保东西存在 if ok, _ := topo.Exist(servicePath); !ok { topo.CreateDir(servicePath) } // 用来从zookeeper获取事件 evtbus := make(chan interface{}) // 2. 将信息添加到Zk中, 并且监控Zk的状态(如果添加失败会怎么样?) endpoint := NewServiceEndpoint(serviceName, serviceId, frontendAddr, workDir, codeUrlVerion) // deployPath // 为了保证Add是干净的,需要先删除,保证自己才是Owner endpoint.DeleteServiceEndpoint(topo) // 如果没有状态,或状态为true, 则上线 if state == nil || state.Get() { endpoint.AddServiceEndpoint(topo) } go func() { for true { // Watch的目的是监控: 当前的zk session的状态, 如果session出现异常,则重新注册 _, err := topo.WatchNode(servicePath, evtbus) if err == nil { // 如果成功添加Watch, 则等待退出,或者ZK Event select { case <-evtExit: return case <-stateChan: // 如何状态变化(则重新注册) endpoint.DeleteServiceEndpoint(topo) if state == nil || state.Get() { endpoint.AddServiceEndpoint(topo) } case e := <-evtbus: event := e.(topozk.Event) if event.State == topozk.StateExpired || event.Type == topozk.EventNotWatching { // Session过期了,则需要删除之前的数据, // 因为当前的session不是之前的数据的Owner endpoint.DeleteServiceEndpoint(topo) if state == nil || state.Get() { endpoint.AddServiceEndpoint(topo) } } } } else { // 如果添加Watch失败,则等待退出,或等待timer接触,进行下一次尝试 timer := time.NewTimer(time.Second * time.Duration(10)) log.ErrorErrorf(err, "Reg Failed, Wait 10 seconds...., %v", err) select { case <-evtExit: return case <-timer.C: // pass } } } }() return endpoint }
// Session是同步处理请求,因此没有必要搞多个 func (s *Session) Serve(d Dispatcher, maxPipeline int) { defer func() { s.Close() log.Infof(Red("==> Session Over: %s, Total %d Ops"), s.RemoteAddress, s.Ops) if err := recover(); err != nil { log.Infof(Red("Catch session error: %v"), err) } }() for true { // 1. 读取请求 request, err := s.ReadFrame() s.Ops += 1 // 读取出错,直接退出 if err != nil { err1, ok := err.(thrift.TTransportException) if !ok || err1.TypeId() != thrift.END_OF_FILE { log.ErrorErrorf(err, Red("ReadFrame Error: %v"), err) } // r.Recycle() return } var r *Request // 2. 处理请求 r, err = s.handleRequest(request, d) if err != nil { // r.Recycle() // 出错之后也要主动返回数据 log.ErrorErrorf(err, Red("handleRequest Error: %v"), err) r.Response.Err = err } // 3. 等待请求处理完毕 s.handleResponse(r) // 4. 将结果写回给Client if s.verbose { log.Debugf("[%s]Session#loopWriter --> client FrameSize: %d", r.Service, len(r.Response.Data)) } // 5. 将请求返回给Client, r.Response.Data ---> Client _, err = s.TBufferedFramedTransport.Write(r.Response.Data) r.Recycle() // 重置: Request if err != nil { log.ErrorErrorf(err, "Write back Data Error: %v", err) return } // 6. Flush err = s.TBufferedFramedTransport.FlushBuffer(true) // len(tasks) == 0 if err != nil { log.ErrorErrorf(err, "Write back Data Error: %v", err) return } // r.Recycle() } }
// // 如何处理后端服务的变化呢? // func (s *BackService) WatchBackServiceNodes() { s.evtbus = make(chan interface{}, 2) servicePath := s.topo.ProductServicePath(s.serviceName) go func() { for !s.stop.Get() { serviceIds, err := s.topo.WatchChildren(servicePath, s.evtbus) if err == nil { // 如何监听endpoints的变化呢? addressMap := make(map[string]bool, len(serviceIds)) for _, serviceId := range serviceIds { log.Printf(Green("---->Find Endpoint: %s for Service: %s"), serviceId, s.serviceName) endpointInfo, err := GetServiceEndpoint(s.topo, s.serviceName, serviceId) if err != nil { log.ErrorErrorf(err, "Service Endpoint Read Error: %v\n", err) } else { log.Printf(Green("---->Add endpoint %s To Service %s"), endpointInfo.Frontend, s.serviceName) if strings.Contains(endpointInfo.Frontend, ":") { addressMap[endpointInfo.Frontend] = true } else if s.productName == TEST_PRODUCT_NAME { // unix domain socket只在测试的时候可以使用(因为不能实现跨机器访问) addressMap[endpointInfo.Frontend] = true } } } for addr, _ := range addressMap { conn, ok := s.addr2Conn[addr] if ok && !conn.IsMarkOffline.Get() { continue } else { // 创建新的连接(心跳成功之后就自动加入到 s.activeConns 中 s.addr2Conn[addr] = NewBackendConn(addr, s, s.serviceName, s.verbose) } } for addr, conn := range s.addr2Conn { _, ok := addressMap[addr] if !ok { conn.MarkOffline() // 删除: 然后等待Conn自生自灭 delete(s.addr2Conn, addr) } } // 等待事件 <-s.evtbus } else { log.WarnErrorf(err, "zk read failed: %s", servicePath) // 如果读取失败则,则继续等待5s time.Sleep(time.Duration(5) * time.Second) } } }() }
func (p *ThriftLoadBalanceServer) Run() { // // 1. 创建到zk的连接 // 127.0.0.1:5555 --> 127_0_0_1:5555 exitSignal := make(chan os.Signal, 1) signal.Notify(exitSignal, syscall.SIGTERM, syscall.SIGINT, syscall.SIGKILL) // syscall.SIGKILL // kill -9 pid // kill -s SIGKILL pid 还是留给运维吧 // // 注册服务 evtExit := make(chan interface{}) // 初始状态为不上线 var state atomic2.Bool state.Set(false) stateChan := make(chan bool) serviceEndpoint := RegisterService(p.serviceName, p.frontendAddr, p.lbServiceName, p.topo, evtExit, p.config.WorkDir, p.config.CodeUrlVersion, &state, stateChan) // var suideTime time.Time // isAlive := true // 3. 读取后端服务的配置 var transport thrift.TServerTransport var err error isUnixDomain := false // 127.0.0.1:9999(以:区分不同的类型) if !strings.Contains(p.frontendAddr, ":") { if rpc_utils.FileExist(p.frontendAddr) { os.Remove(p.frontendAddr) } transport, err = rpc_utils.NewTServerUnixDomain(p.frontendAddr) isUnixDomain = true } else { transport, err = thrift.NewTServerSocket(p.frontendAddr) } if err != nil { log.ErrorErrorf(err, "Server Socket Create Failed: %v", err) panic(fmt.Sprintf("Invalid FrontendAddress: %s", p.frontendAddr)) } err = transport.Listen() if err != nil { log.ErrorErrorf(err, "Server Socket Create Failed: %v", err) panic(fmt.Sprintf("Binding Error FrontendAddress: %s", p.frontendAddr)) } ch := make(chan thrift.TTransport, 4096) defer close(ch) // 等待后端服务起来 waitTicker := time.NewTicker(time.Second) // 等待上线采用的策略: // 1. 检测到有效的Worker注册之后,再等5s即可像zk注册; 避免了Worker没有连接上来,就有请求过来 // 2. 一旦注册之后,就不再使用该策略;避免服务故障时,lb频繁更新zk, 导致proxy等频繁读取zk START_WAIT: for true { select { case <-waitTicker.C: if p.backendService.Active() <= 0 { log.Infof("Sleep Waiting for back Service to Start") time.Sleep(time.Second) } else { break START_WAIT } case <-exitSignal: // 直接退出 transport.Interrupt() transport.Close() return } } log.Infof("Stop Waiting") // 停止: waitTicker, 再等等就继续了 waitTicker.Stop() time.Sleep(time.Second * 5) log.Infof("Begin to Reg To Zk...") state.Set(true) stateChan <- true // 强制退出? TODO: Graceful退出 go func() { <-exitSignal // 通知RegisterService终止循环 evtExit <- true log.Info(Green("Receive Exit Signals....")) serviceEndpoint.DeleteServiceEndpoint(p.topo) start := time.Now().Unix() for true { // 如果5s内没有接受到新的请求了,则退出 now := time.Now().Unix() if now-p.lastRequestTime.Get() > 5 { log.Printf(Red("[%s]Graceful Exit..."), p.serviceName) break } else { log.Printf(Cyan("[%s]Sleeping %d seconds before Exit...\n"), p.serviceName, now-start) time.Sleep(time.Second) } } transport.Interrupt() transport.Close() }() go func() { var address string for c := range ch { // 为每个Connection建立一个Session socket, ok := c.(rpc_utils.SocketAddr) if ok { if isUnixDomain { address = p.frontendAddr } else { address = socket.Addr().String() } } else { address = "unknow" } x := NewNonBlockSession(c, address, p.verbose, &p.lastRequestTime) // Session独立处理自己的请求 go x.Serve(p.backendService, 1000) } }() // Accept什么时候出错,出错之后如何处理呢? for { c, err := transport.Accept() if err != nil { close(ch) break } else { ch <- c } } }
// // 两参数是必须的: ProductName, zkAddress, frontAddr可以用来测试 // func (p *ProxyServer) Run() { var transport thrift.TServerTransport var err error log.Printf(Magenta("Start Proxy at Address: %s"), p.proxyAddr) // 读取后端服务的配置 isUnixDomain := false if !strings.Contains(p.proxyAddr, ":") { if rpc_utils.FileExist(p.proxyAddr) { os.Remove(p.proxyAddr) } transport, err = rpc_utils.NewTServerUnixDomain(p.proxyAddr) isUnixDomain = true } else { transport, err = thrift.NewTServerSocket(p.proxyAddr) } if err != nil { log.ErrorErrorf(err, "Server Socket Create Failed: %v, Front: %s", err, p.proxyAddr) } // 开始监听 // transport.Open() transport.Listen() ch := make(chan thrift.TTransport, 4096) defer close(ch) defer func() { log.Infof(Red("==> Exit rpc_proxy")) if err := recover(); err != nil { log.Infof("Error rpc_proxy: %s", err) } }() go func() { var address string for c := range ch { // 为每个Connection建立一个Session socket, ok := c.(rpc_utils.SocketAddr) if isUnixDomain { address = p.proxyAddr } else if ok { address = socket.Addr().String() } else { address = "unknow" } x := NewSession(c, address, p.verbose) // Session独立处理自己的请求 go x.Serve(p.router, 1000) } }() // Accept什么时候出错,出错之后如何处理呢? for { c, err := transport.Accept() if err != nil { log.ErrorErrorf(err, "Accept Error: %v", err) break } else { ch <- c } } }
// // 数据: 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 }