今天给大家推荐一个 Go 语音实现的 gRPC 代理/网关,配置简单、开箱即用。先和大家介绍一下负载原理:
gRPC 负载均衡和反向代理
代理基本原理:
- 基于TCP启动一个gRPC代理服务 ;
- 将gRPC请求的服务拦截到转发代理的一个函数中执行 ;
- 接收客户端的请求,处理业务指标后转发给服务端 ;
- 接收服务端的响应,处理业务指标后转发给客户端 ;
gRPC的客户端将所有的请求都发给 gRPC Server Proxy,这个代理网关实现请求转发,将gRPC Client的请求流转发到gRPC 服务实现的节点上。并将服务处理结果响应返回给客户端 。
HandleStream 中 unknownService
在 gRPC 框架代码中的 HandleStream 存在两类服务,一类是已知服务 knownService, 第二类是 unknownService ;已知服务 knownService 就是 gRPC 服务端代码注册到 gRPC 框架中的服务,叫做已知服务,其他没有注册的服务叫做未知服务,这个未知服务 unknownService 就是我们实现 gRPC 服务代码的关键所在;
要实现 gRPC 服务代理,我们在创建 gRPC 服务 grpc.NewServer 时,传递一个未知服务的 Handler,将未知服务的处理进行接管,然后通过注册的这个 Handler 实现 gRPC 代理转发的逻辑.
GrpcProxyTransport 接口
GrpcProxyTransport 是自定义的 StreamDirector,将 unknownService 的请求由 GrpcProxyTransport 层处理(这里有实现比较负责的连接池 + 负载均衡 的设计),提供代理和负载的能力;
基于如上描述,gRPC 代理的原理如下:
- 创建 gRPC 服务时,注册一个未知服务处理器 Handler 和一个自定义的编码 Codec 编码和解码,此处使用 proto 标准的 Codec;
- 这个 Handle 给业务方预留一个 director 的接口,用于代理重定向转发的 gRPC 连接获取,这样 proxy 就可以通过 redirector 得到 gRPCServer 的 gRPC 连接。
- Proxy 接收 gRPC 客户端的连接,并使用 gRPC 的 RecvMsg 方法,接收客户端的消息请求 ;
- Proxy 将接收到的 gRPC 客户端消息请求,通过 SendHeader 和 SendMsg 方法发送给 gRPC 服务端 ;
- 同样的方法,RecvMsg 接收 gRPC 服务端的响应消息,使用 SendMsg 发送给 gRPC 客户端。
至此 gRPC 代理服务就完成了消息的转发功能,限流,权限等功能可以通过转发的功能进行拦截处理 ;
如图所示
gRPC Proxy:
gRPC Proxy 流程图:
均衡策略
目前支持 gRPC 均衡策略是:
- 加权随机 ;
- 最小连接数 ;
配置说明
proxy:
setting:
LISTEN_PROXY_ADDR: '0.0.0.0'
proxy_list:
- PROXY_NAME: 'default'
ENABLED: true
POOL_ENABLED: true
DIAL_TIMEOUT: 5 # second
BACKOFF_MAX_DELAY: 3 # second
KEEPALIVE_TIME: 5 # second
KEEPALIVE_TIMEOUT: 10 # second
REQUEST_IDLE_TIME: 10 # second
REQUEST_MAX_LIFE: 60 # second
REQUEST_TIMEOUT: 3 # second
POOL_MODEL: 1 # default 0 : STRICT_MODE, 1: LOOSE_MODE
PROXY_MODEL: 'randomWeight' # minConn or randomWeight
GRPC_REQUEST_REUSABLE: true # 连接是否复用
DEFAULT_GRPC_CONN_NUM: 10 # 默认创建的连接数
PROXY_PORT: '30880'
GRPC_PROXY_ENDPOINTS: # 负载的 endpoints 列表, "#" 号后面是权重
- 172.18.160.84:30880#10
- 172.18.160.84:30880#10
- PROXY_NAME : 为 proxy 名称
- ENABLED: 是否启用此 proxy
- KEEPALIVE_TIME: 如果连接没有被激活,每隔多长时间发送 pings
- KEEPALIVE_TIMEOUT: wait 1 second for ping ack before considering the connection dead
- REQUEST_TIMEOUT: 请求超时设置
- GRPC_REQUEST_REUSABLE: 是否复用连接
- LISTEN_PROXY_ADDR: synapsor 本地监听 ip
- DEFAULT_GRPC_CONN_NUM:建立连接的默认连接数 (会自动进行扩增)
- EXPOSE_PROXY_PORT: synapsor 暴露的端口
- GRPC_PROXY_ENDPOINTS: 负载的 endpoint 列表
- PROXY_MODEL: 负载的模式(minConn:最小连接数,适用于流量控制,流式连接; randomWeight: 加权随机,适用于非流控场景)
支持多个端口负载多个 endpoint 列表
拉取代码后即可运行
运行
配置好 proxy endpoints 后,执行下面命令
docker
docker-compose up -d
k8s
kubectl apply -f deployments/kubernetes/
FAQ
Q: received prior goaway: code: ENHANCE_YOUR_CALM, debug data: “too_many_pings”
A: grpc client 的keepalive 用来检测 client 创建的grpc channel 连接是不是可用的,如果超时,就会关掉这个channel 的连接 ;grpc server 的keepalive 用来检测 server 创建的grpc channel 连接是不是可用的,如果超时,就会关掉这个channel 的连接 ;在这里还需要特别注意一个问题, grpc client 的keepalive 的 时间设定 需要在server 允许范围内,否则,server 会发送一个GOAWAY 消息,把和client 的连接强制关掉 。
Q: code = Cancelled desc = Cancelled on the server side
A: 客户端建立连接后,超过一定时间没有发送数据 (包括 ping 数据) 会被 server 端主动断开连接 ;
- By 斜杆青年