hello-world例子中的server/main.go中我们看到生成一个server的代码段如下:

  1. lis, err := net.Listen("tcp", port)
  2. if err != nil {
  3. log.Fatalf("failed to listen: %v", err)
  4. }
  5. s := grpc.NewServer()
  6. pb.RegisterGreeterServer(s, &server{})
  7. if err := s.Serve(lis); err != nil {
  8. log.Fatalf("failed to serve: %v", err)
  9. }

从这段代码逻辑可以看出创建一个server大致分为如下几步:

  • 创建一个新的server(grpc.NewServer())
  • server进行注册
  • 调用方法监听端口

创建server

  1. func NewServer(opt ...ServerOption) *Server {
  2. opts := defaultServerOptions // 1.
  3. for _, o := range opt {
  4. o.apply(&opts)
  5. }
  6. s := &Server{ // 2.
  7. lis: make(map[net.Listener]bool),
  8. opts: opts,
  9. conns: make(map[transport.ServerTransport]bool),
  10. m: make(map[string]*service),
  11. quit: grpcsync.NewEvent(),
  12. done: grpcsync.NewEvent(),
  13. czData: new(channelzData),
  14. }
  15. chainUnaryServerInterceptors(s) // 3.
  16. chainStreamServerInterceptors(s)
  17. s.cv = sync.NewCond(&s.mu)
  18. if EnableTracing {
  19. _, file, line, _ := runtime.Caller(1)
  20. s.events = trace.NewEventLog("grpc.Server", fmt.Sprintf("%s:%d", file, line))
  21. }
  22. if channelz.IsOn() {
  23. s.channelzID = channelz.RegisterServer(&channelzServer{s}, "")
  24. }
  25. return s
  26. }

首先该方法的入参是一些服务器的可选参数,ServerOption本身是一个接口,里面有一个apply方法

  1. type ServerOption interface {
  2. apply(*serverOptions)
  3. }

根据官方给出的注释,我们可以看到这个接口里的方法主要是对server设置一些可选参数,比如codec,或者是参数的生命周期等。而serverOptions这个结构体也恰恰定义的是这些服务器的参数。

回到注释1的位置,那么首先我们的程序就是设置了必要的服务器参数,具体的内容如下:

  1. var defaultServerOptions = serverOptions{
  2. maxReceiveMessageSize: defaultServerMaxReceiveMessageSize,
  3. maxSendMessageSize: defaultServerMaxSendMessageSize,
  4. connectionTimeout: 120 * time.Second,
  5. writeBufferSize: defaultWriteBufSize,
  6. readBufferSize: defaultReadBufSize,
  7. }

比如说默认的最大可接收和发送的消息大小,连接的超时时间和Buffer的大小。

然后进入的for循环就是将服务器参数都设置到我们的optionServers这个结构体中。

再向下走进入到注释2的位置,就是对我们的Server结构体做了设置,Server的结构如下:

  1. type Server struct {
  2. opts serverOptions
  3. mu sync.Mutex // 互斥锁
  4. lis map[net.Listener]bool // listener map
  5. conns map[transport.ServerTransport]bool //connections map
  6. serve bool // 是否在处理请求的状态位
  7. drain bool
  8. cv *sync.Cond // signaled when connections close for GracefulStop
  9. m map[string]*service // service name -> service info
  10. events trace.EventLog
  11. quit *grpcsync.Event
  12. done *grpcsync.Event
  13. channelzRemoveOnce sync.Once
  14. serveWG sync.WaitGroup // counts active Serve goroutines for GracefulStop
  15. channelzID int64 // channelz unique identification number
  16. czData *channelzData
  17. }

可以看到,这里面比较重要的有3个map,分别存储的是listener的信息,connection的信息以及提供的service的信息。而其他的字段主要是提供了某些服务器的状态信息或者并发控制的功能。

那么首先对于存储listener的map而言,listener本质是一个接口,里面提供了Accept(),Close(),Addr()三个方法,分别提供是服务器准备进行连接,和关闭listener,以及返回listener网络地址的功能。

对存储service的map而言,service的结构如下:

  1. type service struct {
  2. server interface{} // the server for service methods
  3. md map[string]*MethodDesc
  4. sd map[string]*StreamDesc
  5. mdata interface{}
  6. }

server接口里存放的是该服务器所提供的service方法,而下面两个map则是存储了method和stream流的服务信息。

  1. type MethodDesc struct {
  2. MethodName string
  3. Handler methodHandler
  4. }
  5. type StreamDesc struct {
  6. StreamName string
  7. Handler StreamHandler
  8. // At least one of these is true.
  9. ServerStreams bool
  10. ClientStreams bool
  11. }

其中每一个struct里面都有一个handler来对调用的方法进行处理。

回到主流程的注释3的位置,可以看到有两个拦截器,第一个拦截器主要是将我们定义的server端的拦截器最终都用一个拦截器链进行规整:(具体内容见注释)

  1. func chainUnaryServerInterceptors(s *Server) {
  2. // Prepend opts.unaryInt to the chaining interceptors if it exists, since unaryInt will
  3. // be executed before any other chained interceptors.
  4. // 这几步主要是检查一下拦截器的个数,如果方法unary拦截器数组不是空的话,就要把这些拦截器继续添加到我们的拦截器链上
  5. interceptors := s.opts.chainUnaryInts //
  6. if s.opts.unaryInt != nil {
  7. interceptors = append([]UnaryServerInterceptor{s.opts.unaryInt}, s.opts.chainUnaryInts...)
  8. }
  9. var chainedInt UnaryServerInterceptor
  10. if len(interceptors) == 0 {
  11. chainedInt = nil
  12. } else if len(interceptors) == 1 {
  13. chainedInt = interceptors[0]
  14. } else {
  15. // 如果拦截器数量大于1,那么会递归的生成一个拦截器链
  16. chainedInt = func(ctx context.Context, req interface{}, info *UnaryServerInfo, handler UnaryHandler) (interface{}, error) {
  17. return interceptors[0](ctx, req, info, getChainUnaryHandler(interceptors, 0, info, handler))
  18. }
  19. }
  20. s.opts.unaryInt = chainedInt
  21. }

其中,getChainUnaryHandler方法的逻辑需要看一下:

  1. func getChainUnaryHandler(interceptors []UnaryServerInterceptor, curr int, info *UnaryServerInfo, finalHandler UnaryHandler) UnaryHandler {
  2. if curr == len(interceptors)-1 {
  3. return finalHandler
  4. }
  5. return func(ctx context.Context, req interface{}) (interface{}, error) {
  6. return interceptors[curr+1](ctx, req, info, getChainUnaryHandler(interceptors, curr+1, info, finalHandler))
  7. }
  8. }

在本段逻辑里,首先判断当前的curr指针是不是整个拦截器链的末尾,如果是的话就会返回最尾端的handler,否则就不断的递归,从而生成一条拦截器链。
chainStreamServerInterceptorschainUnaryServerInterceptors方法类似,这里不再赘述。

server注册

继续回到主线,在完成了相应的server设置之后,就要对server进行注册,跟随
pb.RegisterGreeterServer(s, &server{})方法一路进入到RegisterService方法,即:

  1. func (s *Server) RegisterService(sd *ServiceDesc, ss interface{}) {
  2. ht := reflect.TypeOf(sd.HandlerType).Elem()
  3. st := reflect.TypeOf(ss)
  4. if !st.Implements(ht) {
  5. grpclog.Fatalf("grpc: Server.RegisterService found the handler of type %v that does not satisfy %v", st, ht)
  6. }
  7. s.register(sd, ss)
  8. }

首先就是通过反射来获取server当中handler链中每个handler的类型,再获自己定义的service的类型,紧接着就是判断一下是否自定义的service的类型实现了我们要求的server里面handler的类型,如果是的话就进入register的逻辑。

  1. func (s *Server) register(sd *ServiceDesc, ss interface{}) {
  2. s.mu.Lock()
  3. defer s.mu.Unlock()
  4. s.printf("RegisterService(%q)", sd.ServiceName)
  5. if s.serve {
  6. grpclog.Fatalf("grpc: Server.RegisterService after Server.Serve for %q", sd.ServiceName)
  7. }
  8. if _, ok := s.m[sd.ServiceName]; ok {
  9. grpclog.Fatalf("grpc: Server.RegisterService found duplicate service registration for %q", sd.ServiceName)
  10. }
  11. srv := &service{
  12. server: ss,
  13. md: make(map[string]*MethodDesc),
  14. sd: make(map[string]*StreamDesc),
  15. mdata: sd.Metadata,
  16. }
  17. for i := range sd.Methods {
  18. d := &sd.Methods[i]
  19. srv.md[d.MethodName] = d
  20. }
  21. for i := range sd.Streams {
  22. d := &sd.Streams[i]
  23. srv.sd[d.StreamName] = d
  24. }
  25. s.m[sd.ServiceName] = srv
  26. }

启动Serve

对于通常的C/S架构的通信,普遍的实现都是server端不断地嗅探本端口是不是有连接请求,如果有client端进行连接,那么就握手建立连接,然后client通过调用相应的方法和参数对server的service进行调用,这个请求就对打到server端的handler处来进行处理。所以,对 server 端来说,主要是了解其如何实现监听,如何为请求分配不同的 handler 和 回写响应数据。来看一下具体的Serve方法

  1. func (s *Server) Serve(lis net.Listener) error {
  2. s.mu.Lock()
  3. s.printf("serving")
  4. s.serve = true
  5. if s.lis == nil {
  6. // Serve called after Stop or GracefulStop.
  7. s.mu.Unlock()
  8. lis.Close()
  9. return ErrServerStopped
  10. }
  11. s.serveWG.Add(1)
  12. defer func() {
  13. s.serveWG.Done()
  14. if s.quit.HasFired() {
  15. // Stop or GracefulStop called; block until done and return nil.
  16. <-s.done.Done()
  17. }
  18. }()
  19. ls := &listenSocket{Listener: lis}
  20. s.lis[ls] = true
  21. if channelz.IsOn() {
  22. ls.channelzID = channelz.RegisterListenSocket(ls, s.channelzID, lis.Addr().String())
  23. }
  24. s.mu.Unlock()
  25. defer func() {
  26. s.mu.Lock()
  27. if s.lis != nil && s.lis[ls] {
  28. ls.Close()
  29. delete(s.lis, ls)
  30. }
  31. s.mu.Unlock()
  32. }()
  33. var tempDelay time.Duration // how long to sleep on accept failure
  34. for {
  35. rawConn, err := lis.Accept() // 4
  36. if err != nil {
  37. if ne, ok := err.(interface {
  38. Temporary() bool
  39. }); ok && ne.Temporary() {
  40. if tempDelay == 0 {
  41. tempDelay = 5 * time.Millisecond
  42. } else {
  43. tempDelay *= 2
  44. }
  45. if max := 1 * time.Second; tempDelay > max {
  46. tempDelay = max
  47. }
  48. s.mu.Lock()
  49. s.printf("Accept error: %v; retrying in %v", err, tempDelay)
  50. s.mu.Unlock()
  51. timer := time.NewTimer(tempDelay)
  52. select {
  53. case <-timer.C:
  54. case <-s.quit.Done():
  55. timer.Stop()
  56. return nil
  57. }
  58. continue
  59. }
  60. s.mu.Lock()
  61. s.printf("done serving; Accept = %v", err)
  62. s.mu.Unlock()
  63. if s.quit.HasFired() {
  64. return nil
  65. }
  66. return err
  67. }
  68. tempDelay = 0
  69. // Start a new goroutine to deal with rawConn so we don't stall this Accept
  70. // loop goroutine.
  71. //
  72. // Make sure we account for the goroutine so GracefulStop doesn't nil out
  73. // s.conns before this conn can be added.
  74. s.serveWG.Add(1) // 5.
  75. go func() {
  76. s.handleRawConn(rawConn)
  77. s.serveWG.Done()
  78. }()
  79. }
  80. }

从注释4开始,我们看到程序进入循环然后监听对应的端口。紧接在看到注释5我们发现程序起了一个goroutine去调用handleRawConn方法,进一步跟踪进去:

  1. func (s *Server) handleRawConn(rawConn net.Conn) {
  2. // ...
  3. conn, authInfo, err := s.useTransportAuthenticator(rawConn)
  4. // ...
  5. // Finish handshaking (HTTP2)
  6. st := s.newHTTP2Transport(conn, authInfo)
  7. if st == nil {
  8. return
  9. }
  10. // ...
  11. go func() {
  12. s.serveStreams(st)
  13. s.removeConn(st)
  14. }()
  15. }

我将不重要的地方进行了省略,可以看到在本方法内,确实是通过建立HTTP2的握手来实现了连接的建立,然后程序又开了一个goroutine来调用了serveStreams方法:

  1. func (s *Server) serveStreams(st transport.ServerTransport) {
  2. defer st.Close()
  3. var wg sync.WaitGroup
  4. st.HandleStreams(func(stream *transport.Stream) {
  5. wg.Add(1)
  6. go func() {
  7. defer wg.Done()
  8. s.handleStream(st, stream, s.traceInfo(st, stream))
  9. }()
  10. }, func(ctx context.Context, method string) context.Context {
  11. if !EnableTracing {
  12. return ctx
  13. }
  14. tr := trace.New("grpc.Recv."+methodFamily(method), method)
  15. return trace.NewContext(ctx, tr)
  16. })
  17. wg.Wait()
  18. }

在这里依然是调用了handleStreams方法:

  1. func (s *Server) handleStream(t transport.ServerTransport, stream *transport.Stream, trInfo *traceInfo) {
  2. sm := stream.Method()
  3. pos := strings.LastIndex(sm, "/")
  4. if pos == -1 {
  5. ...
  6. }
  7. service := sm[:pos]
  8. method := sm[pos+1:]
  9. srv, knownService := s.m[service]
  10. if knownService {
  11. if md, ok := srv.md[method]; ok {
  12. s.processUnaryRPC(t, stream, srv, md, trInfo)
  13. return
  14. }
  15. if sd, ok := srv.sd[method]; ok {
  16. s.processStreamingRPC(t, stream, srv, sd, trInfo)
  17. return
  18. }
  19. }
  20. ...
  21. }

stream.Method()就是这个服务的入口,例如”/SCDPSshService.Common/RpcForTranspondCommand”
在这里,果然,程序根据 serviceName 去 server 中的 service map,也就是 m 这个字段里去取出 handler 进行处理。我们 hello world 这个 demo 的请求不涉及到 stream ,所以直接取出 handler ,然后传给 processUnaryRPC 这个方法进行处理。

所以我们进一步跟进processUnaryRPC方法:

  1. func (s *Server) processUnaryRPC(t transport.ServerTransport, stream *transport.Stream, srv *service, md *MethodDesc, trInfo *traceInfo) (err error) {
  2. // ...
  3. sh := s.opts.statsHandler
  4. if sh != nil {
  5. beginTime := time.Now()
  6. begin := &stats.Begin{
  7. BeginTime: beginTime,
  8. }
  9. sh.HandleRPC(stream.Context(), begin)
  10. defer func() {
  11. end := &stats.End{
  12. BeginTime: beginTime,
  13. EndTime: time.Now(),
  14. }
  15. if err != nil && err != io.EOF {
  16. end.Error = toRPCErr(err)
  17. }
  18. sh.HandleRPC(stream.Context(), end)
  19. }()
  20. }
  21. // ...
  22. if err := s.sendResponse(t, stream, reply, cp, opts, comp); err != nil {
  23. if err == io.EOF {
  24. // The entire stream is done (for unary RPC only).
  25. return err
  26. }
  27. if s, ok := status.FromError(err); ok {
  28. if e := t.WriteStatus(stream, s); e != nil {
  29. grpclog.Warningf("grpc: Server.processUnaryRPC failed to write status: %v", e)
  30. }
  31. } else {
  32. switch st := err.(type) {
  33. case transport.ConnectionError:
  34. // Nothing to do here.
  35. default:
  36. panic(fmt.Sprintf("grpc: Unexpected error (%T) from sendResponse: %v", st, st))
  37. }
  38. }
  39. if binlog != nil {
  40. h, _ := stream.Header()
  41. binlog.Log(&binarylog.ServerHeader{
  42. Header: h,
  43. })
  44. binlog.Log(&binarylog.ServerTrailer{
  45. Trailer: stream.Trailer(),
  46. Err: appErr,
  47. })
  48. }
  49. return err
  50. }
  51. // ...
  52. }

我们发现了对handler方法的调用和response的回写

转自:https://juejin.cn/post/6937992447599312904