您现在的位置是:首页 >技术杂谈 >gRPC-Go源码解读三 服务端处理流程分析网站首页技术杂谈

gRPC-Go源码解读三 服务端处理流程分析

yyyyyyyuande 2023-05-29 20:00:02
简介gRPC-Go源码解读三 服务端处理流程分析

        相较于Client端的复杂处理流程,Server端相对来说简单了很多,核心就是创建个TCP套接字并监听,收到客户端连接请求则起个go协程处理,子协程根据请求中的服务名和方法名调用对应的服务方法处理,处理完成之后则返回响应。整个过程不涉及服务发现和负载均衡,因此代码相对简洁。

        下面以gRPC-Go 1.54.0-dev版本中 examples/helloworld为例,先看看pb的服务定义:

package helloworld;

// The greeting service definition.
service Greeter {
  // Sends a greeting
  rpc SayHello (HelloRequest) returns (HelloReply) {}
}

// The request message containing the user's name.
message HelloRequest {
  string name = 1;
}

// The response message containing the greetings
message HelloReply {
  string message = 1;
}

该文件定义了Greeter服务,只包含一个Unary方法SayHello,请求响应参数都只包含一个字符串。

下面看下服务端的代码部分:


var (
	port = flag.Int("port", 50051, "The server port")
)

// server 用来实现helloworld.GreeterServer,默认继承了UnimplementedGreeterServer
type server struct {
	pb.UnimplementedGreeterServer
}

// SayHello 具体实现,就是将请求中的字符串加个Hello 返回回去
func (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {
	log.Printf("Received: %v", in.GetName())
	return &pb.HelloReply{Message: "Hello " + in.GetName()}, nil
}

func main() {
    // 启动参数解析
	flag.Parse()

    // 创建TCP套接字
	lis, err := net.Listen("tcp", fmt.Sprintf(":%d", *port))
	if err != nil {
		log.Fatalf("failed to listen: %v", err)
	}

	// 创建grpcServer
	s := grpc.NewServer()

	// 将server注册到grpcServer里
	pb.RegisterGreeterServer(s, &server{})

	log.Printf("server listening at %v", lis.Addr())
	// 开始监听客户端请求并服务
	if err := s.Serve(lis); err != nil {
		log.Fatalf("failed to serve: %v", err)
	}
}

        主函数代码不多,就是创建套接字和grpcServer实例,然后注册实际的业务实现,之后就可以接收客户端请求开始服务了。 

        下面以流程图的方式展示整个调用链路过程,相对于粘贴代码,流程图看起来更直观、简洁。

        

图1 Server端处理流程

         如图所示,红色部分表示主函数,绿色部分表示处理Client链接的子协程。Server每收到一个TCP连接请求则分配一个协程处理。子协程主要工作就是处理HTTP2 Frame,当收到Header Frame后则根据Header头中携带的path域找到对应Client请求的服务名和方法名,然后从grpcServer中查找程序启动时注册的服务,找到则处理,找不到则返回一个错误消息是"unknown service" 或者"unknown method" Header Frame,关于传输层其它部分的处理(如http2初始化、流量控制、数据读写等)可以参考前面几篇博文。

        下面看下请求的抓包例子:

图2 Client请求HeaderFrame

         图2显示了Client的请求头,可以看到path域携带了所请求的服务名和方法名。当子协程据此查找注册的服务方法来处理。

           gRPC Server 和Client在传输层复用了大部分代码,比如loopy,看了之前对Client请求的分析,再来看本篇就会发现简单许多。

        

风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。