Golang 5分钟理解gRPC服务端源码

896 阅读2分钟

源码架构分析图

gRpc-Server.png

启动gRPC服务

import "google.golang.org/grpc"
func main() {
    lis, err := net.Listen("tcp", port)
    if err != nil {
        log.Fatalf("failed to listen: %v", err)
    }
    s := grpc.NewServer()
    pb.RegisterGreeterServer(s, &server{})
    if err := s.Serve(lis); err != nil {
        log.Fatalf("failed to serve: %v", err)
    }
}

server 端连接的建立主要包括以下三步

  1. 创建 Server
  2. 注册 Server
  3. 调用 Server() 监听端口并处理请求

创建Server

//NewServer创建一个gRPC服务器,该服务器没有注册服务,并且还没有开始接受请求。
func NewServer(opt ...ServerOption) *Server {
   opts := defaultServerOptions
   for _, o := range opt {
      o.apply(&opts)
   }
   s := &Server{
      lis:      make(map[net.Listener]bool),
      opts:     opts,
      conns:    make(map[string]map[transport.ServerTransport]bool),
      services: make(map[string]*serviceInfo),
      quit:     grpcsync.NewEvent(),
      done:     grpcsync.NewEvent(),
      czData:   new(channelzData),
   }
   chainUnaryServerInterceptors(s)
   chainStreamServerInterceptors(s)
   s.cv = sync.NewCond(&s.mu)
   //...
   return s
}

注册Server

这里我们以 helloworld.proto 为例:

syntax = "proto3";

option go_package = "proto/helloworld";

message GreeterReq {
}

message GreeterRsp {
}

service Greeter {
  rpc SayHello(GreeterReq)returns (GreeterRsp);
  rpc SayHelloAgain(GreeterReq)returns(GreeterRsp);
}

server 的注册调用了 RegisterGreeterServer 方法,这个方法是 pb.go 文件里面的,如下:

func RegisterGreeterServer(s grpc.ServiceRegistrar, srv GreeterServer) {
   s.RegisterService(&Greeter_ServiceDesc, srv)
}

这个方法调用了 server.RegisterService方法,然后传入了一个ServiceDesc的数据结构,如下:

// Greeter service 的描述文件
var Greeter_ServiceDesc = grpc.ServiceDesc{
   ServiceName: "Greeter",
   HandlerType: (*GreeterServer)(nil),
   Methods: []grpc.MethodDesc{
      {
         MethodName: "SayHello",
         Handler:    _Greeter_SayHello_Handler,
      },
      {
         MethodName: "SayHelloAgain",
         Handler:    _Greeter_SayHelloAgain_Handler,
      },
   },
   Streams:  []grpc.StreamDesc{},
   Metadata: "helloworld.proto",
}

RegisterService 这个方法,可以看到主要是调用了 register 方法,register 方法则按照方法名为 key,将方法注入到 serverservice map 中,在处理不同的 rpc 请求时根据不用的 serviceName 取出不同的 handler 进行处理。

func (s *Server) RegisterService(sd *ServiceDesc, ss interface{}) {
   if ss != nil {
      ht := reflect.TypeOf(sd.HandlerType).Elem()
      st := reflect.TypeOf(ss)
      if !st.Implements(ht) {
         logger.Fatalf("grpc: Server.RegisterService found the handler of type %v that does not satisfy %v", st, ht)
      }
   }
   s.register(sd, ss)
}

func (s *Server) register(sd *ServiceDesc, ss interface{}) {
   s.mu.Lock()
   defer s.mu.Unlock()
   s.printf("RegisterService(%q)", sd.ServiceName)
   if s.serve {
      logger.Fatalf("grpc: Server.RegisterService after Server.Serve for %q", sd.ServiceName)
   }
   if _, ok := s.services[sd.ServiceName]; ok {
      logger.Fatalf("grpc: Server.RegisterService found duplicate service registration for %q", sd.ServiceName)
   }
   info := &serviceInfo{
      serviceImpl: ss,
      methods:     make(map[string]*MethodDesc),
      streams:     make(map[string]*StreamDesc),
      mdata:       sd.Metadata,
   }
   for i := range sd.Methods {
      d := &sd.Methods[i]
      info.methods[d.MethodName] = d
   }
   for i := range sd.Streams {
      d := &sd.Streams[i]
      info.streams[d.StreamName] = d
   }
   s.services[sd.ServiceName] = info
}

监听 Server

func (s *Server) Serve(lis net.Listener) error {
    //...
    for {
            rawConn, err := lis.Accept()
            // ...
            s.serveWG.Add(1)
            go func() {
                    s.handleRawConn(rawConn)
                    s.serveWG.Done()
            }()
    }
 }

启动一个新的goroutine来处理rawConn,这样我们就不会暂停这个Accept循环goroutine。


欢迎评论,有疑问或者有其它感兴趣的学习方向我们会为您一一解答