前言
在分布式高并发服务器中,client到server以及server中的多个节点之间的连接往往使用连接池来管理。简单来说就是将提前创建好的连接保存在池中,当有请求到来时,直接使用连接池中的连接对server端访问,省去了创建连接和销毁连接的开销(TCP建立连接时的三次握手和释放连接时的四次挥手),从而提高了性能。
目录
设计原则
- 连接池的扩缩容
- 空闲连接的超时与保活
- 池满的处理机制
连接池的扩缩容
通常连接池属性包含最大空闲连接数和最大活跃连接数。
最大空闲连接数
最大活跃连接数池满的处理机制
扩容
缩容
空闲连接的超时与保活
超时最大空闲超时时间
保活
1、连接池设置一个Ping函数,专门用来做连接的保活。在从池中获取连接的时候,Ping一下服务器,如果得到响应,则连接依然有效,便可继续使用,如果超时无响应,则关闭该连接,生成新的连接,由于每次都要Ping一下,必然会增加延迟。也可以后台用一个线程或者协程定期的执行Ping函数,进行连接的保活,缺点是感知连接的失效会有一定的延迟,从池中仍然有可能获取到失效的连接。
2、客户端加入相应的重试机制。比如重试3次,前两次从池中获取连接执行,如果报的错是失效的连接等有关连接问题的错误,那么第3次从池中获取的时候带上参数,指定获取新建的连接,同时连接池移除前两次获取的失效的连接。
池满的处理机制
连接池不可能无限的容纳连接,当池满时,有两种处理机制:
1、池新建连接,并返回给客户端,当客户端用完时,如果池满则关闭连接,否则放入池中。
2、设置一定的超时时间来等待空闲连接。需要客户端加入重试机制,避免因超时之后获取不到空闲连接产生的错误。
基本原理
- 服务启动时建立连接池。
- 初始化连接池,建立最大空闲连接数个连接。
- 请求到来时,从池中获取一个连接。如果没有空闲连接且连接数没有达到最大活跃连接数,则新建连接;如果达到最大活跃连接数,设置一定的超时时间,等待获取空闲连接。
- 获取到连接后进行通信服务。
- 释放连接,此时是将连接放回连接池,如果池满则关闭连接。
- 释放连接池,关闭所有连接。
GRPC特性
关于GRPC的介绍,不在这里阐述,可阅读深入了解GRPC协议,也可自行Google。这里主要简要说明GRPC的两个特性:多路复用、超时重连。
多路复用MaxConcurrentStreams
超时重连
以Golang的GRPC客户端为例:
GRPC调优
GRPC默认的参数对于传输大数据块来说不够友好,我们需要进行特定参数的调优。
MaxSendMsgSize
MaxRecvMsgSize
InitialWindowSize
InitialConnWindowSize
KeepAliveTime
KeepAliveTimeout
PermitWithoutStream
实现细则
基于GRPC的多路复用、超时重连特性,我们很容易实现GRPC连接池。
接口设计
提供简洁的Pool和Conn的接口设计。
连接复用
MaxConcurrentStreams物理连接逻辑连接逻辑连接物理连接MaxConcurrentStreams
扩缩容
扩容最大空闲连接数MaxConcurrentStreams
缩容
超时保活
基于GRPC的Keepalived特性,我们不需要自己实现保活机制,也无需关注连接池中的连接是否有效,因为就算失效,GRPC会自动重连的,此时只不过耗时会略微增加,即认为除了服务器一直处于down状态等原因,连接池中的连接是始终有效的。
Tips
- 由于使用hash求余,每个GRPC上并发的Stream可能会超过MaxConcurrentStreams。
- 不同场景对应的连接池配置也不一样,需要根据自己的场景压测得出连接池的最佳参数配置。