步骤一:Service API interface
// search.pb.go type SearchServiceServer interface { Search(context.Context, *SearchRequest) (*SearchResponse, error) } func RegisterSearchServiceServer(s *grpc.Server, srv SearchServiceServer) { s.RegisterService(&_SearchService_serviceDesc, srv) }还记得我们平时编写的 Protobuf 吗?在生成出来的 .pb.go 文件中,会定义出 Service APIs interface 的具体实现约束。而我们在 gRPC Server 进行注册时,会传入应用 Service 的功能接口实现,此时生成的 RegisterServer 方法就会保证两者之间的一致性。
步骤二:Service API IDL
你想乱传糊弄一下?不可能的,请乖乖定义与 Protobuf 一致的接口方法。但是那个 &_SearchService_serviceDesc 又有什么作用呢?代码如下:
// search.pb.go var _SearchService_serviceDesc = grpc.ServiceDesc{ ServiceName: "proto.SearchService", HandlerType: (*SearchServiceServer)(nil), Methods: []grpc.MethodDesc{ { MethodName: "Search", Handler: _SearchService_Search_Handler, }, }, Streams: []grpc.StreamDesc{}, Metadata: "search.proto", }这看上去像服务的描述代码,用来向内部表述 “我” 都有什么。涉及如下:
ServiceName:服务名称
HandlerType:服务接口,用于检查用户提供的实现是否满足接口要求
Methods:一元方法集,注意结构内的 Handler 方法,其对应最终的 RPC 处理方法,在执行 RPC 方法的阶段会使用。
Streams:流式方法集
Metadata:元数据,是一个描述数据属性的东西。在这里主要是描述 SearchServiceServer 服务
步骤三:Register Service
func (s *Server) register(sd *ServiceDesc, ss interface{}) { ... srv := &service{ server: ss, md: make(map[string]*MethodDesc), sd: make(map[string]*StreamDesc), mdata: sd.Metadata, } for i := range sd.Methods { d := &sd.Methods[i] srv.md[d.MethodName] = d } for i := range sd.Streams { ... } s.m[sd.ServiceName] = srv }在最后一步中,我们会将先前的服务接口信息、服务描述信息给注册到内部 service 去,以便于后续实际调用的使用。涉及如下:
server:服务的接口信息
md:一元服务的 RPC 方法集
sd:流式服务的 RPC 方法集
mdata:metadata,元数据
小结
在这一章节中,主要介绍的是 gRPC Server 在启动前的整理和注册行为,看上去很简单,但其实一切都是为了后续的实际运行的预先准备。因此我们整理一下思路,将其串联起来看看,如下:
三、监听接下来到了整个流程中,最重要也是大家最关注的监听/处理阶段,核心代码如下:
func (s *Server) Serve(lis net.Listener) error { ... var tempDelay time.Duration for { rawConn, err := lis.Accept() if err != nil { if ne, ok := err.(interface { Temporary() bool }); ok && ne.Temporary() { if tempDelay == 0 { tempDelay = 5 * time.Millisecond } else { tempDelay *= 2 } if max := 1 * time.Second; tempDelay > max { tempDelay = max } ... timer := time.NewTimer(tempDelay) select { case <-timer.C: case <-s.quit: timer.Stop() return nil } continue } ... return err } tempDelay = 0 s.serveWG.Add(1) go func() { s.handleRawConn(rawConn) s.serveWG.Done() }() } }