grpc Server
本文简单阅读源代码,了解grpc server的执行流程,从建立连接,到处理一条请求的过程。
使用方式
使用方式很简单,生成pb,注册建立服务,就可以等待请求了
|
|
建立grpc server流程
NewServer
NewServer进行创建一个grpc服务,初始化一些参数。还可以进行函数选项模式
,来传递初始化的配置。
默认情况下会建立一个以下参数的grpc服务:
- 接受数据最大4M
- 发送数据最大2g
- 连接超时120秒
- 读和写缓存1mb
- 默认一个请求一个goroutine
numServerWorkers
numServerWorkers设定了开启多少个工作协程,如果没设置,则来了一条消息就会处理创建一个goroutine。
如果设置了,会将请求消息
进行分发给这多个worker
|
|
注册函数
利用反射,将具体实现的结构体和与之对应的函数存储到 grpcServer的services
变量中
- key: 结构体名称 (一般在pb文件里会根据proto生成)
- value: 函数信息(调用函数的指针,函数名称,Metadata)
将函数信息存储后,来了一个请求根据请求信息,找到指定的函数,进行调用
|
|
监听
当客户端建立连接,会为其单独创建一个goroutine进行后续的数据传输
|
|
处理一个grpc请求的流程
- 建立连接
- 创建goroutine处理连接
- grpc基于http2,根据tcp连接信息 创建http2 传输结构 newHTTP2Transport
- 创建新的goroutine,将http2传输信息 进行处理
- 经过http2_server.go:455中的处理,从tcp层读取数据进行解析,最后执行此处传递过去的函数指针
- 根据请求信息,找到指定函数。最后进行调用注册的应用层业务
- 找到字典里对应的执行函数
- 如果没找到,则判断unknownStreamDesc 执行,这个一般用来自定义路由
- 判断一元,还是流
- 执行函数
- 将结果写回给对方
- 移除连接
code
|
|
解析请求头数据的细节
- 读取底层tcp数据,最后进行解析
- 解析头数据
- 根据解析后的头数据,进行一系列的设置
- 设置:超时的ctx
- 设置:metadata 存入context中
- 执行函数指针
http2从tcp将数据报转换成http2认识的具体数据。之后grpc将http2的数据封装成grpc用到的stream结构中,还有一些参数timeout
、content-type
等封装到stream中的ctx中,到这里为止还没有对具体的请求数据做任何操作。
|
|
解析请求数据的细节
-
利用反射注册的函数,进行调用,参数传递,传递解析方式,但不会调用
-
调用到pb里注册的函数,在_TspService_Hello_Handler中进行具体处理
- 解析请求信息
- 调用拦截器
- 拦截器过滤后,进行最终的函数调用
-
源码
|
|
Metadata
Metadata是在一次 RPC 调用过程中关于这次调用的信息。 是 key-value的形式。其中 key 是 string 类型, value是[]string。
Metadata 对于 gRPC 本身来说透明, 它使得 client 和 server 能为对方提供本次调用的信息。就像一次 http 请求的 RequestHeader 和 ResponseHeader,http header 的生命周期是一次 http 请求, Metadata 的生命周期则是一次 RPC 调用
|
|
使用
- 发送方
|
|
- 接收方
|
|
注意事项
metadata本意是用来描述调用的信息的:协议的格式、调用方的请求方式、参数、非业务相关信息等。数据相关的不要用metadata进行存储。这样可以在不进行解析传输数据的情况下,依靠metadata进行一些逻辑处理,比如根据metatdata判断数据解析的方式、一些中间服务根据metadata信息,进行面向服务的操作。
拦截器
拦截器(Interceptor) 类似于 HTTP 应用的中间件(Middleware),能够让你在真正调用 RPC 方法前,进行身份认证、日志、限流、异常捕获、参数校验等通用操作,和 Python 的装饰器(Decorator) 的作用基本相同。
客户端发起请求前做一些验证,服务端处理消息前做过滤 grpc服务端和客户端都提供了interceptor功能
- client:发起请求前做统一处理
- server:收到请求,进入具体执行函数之前,对请求做统一处理
调用方式类似链表、调用一个后再调用下一个节点
grpc源码
|
|