前言

在分布式高并发服务器中,client到server以及server中的多个节点之间的连接往往使用连接池来管理。简单来说就是将提前创建好的连接保存在池中,当有请求到来时,直接使用连接池中的连接对server端访问,省去了创建连接和销毁连接的开销(TCP建立连接时的三次握手和释放连接时的四次挥手),从而提高了性能。

目录

设计原则

  • 连接池的扩缩容
  • 空闲连接的超时与保活
  • 池满的处理机制

连接池的扩缩容

通常连接池属性包含最大空闲连接数和最大活跃连接数。

最大空闲连接数
最大活跃连接数池满的处理机制
扩容
缩容

空闲连接的超时与保活

超时最大空闲超时时间
保活

1、连接池设置一个Ping函数,专门用来做连接的保活。在从池中获取连接的时候,Ping一下服务器,如果得到响应,则连接依然有效,便可继续使用,如果超时无响应,则关闭该连接,生成新的连接,由于每次都要Ping一下,必然会增加延迟。也可以后台用一个线程或者协程定期的执行Ping函数,进行连接的保活,缺点是感知连接的失效会有一定的延迟,从池中仍然有可能获取到失效的连接。

2、客户端加入相应的重试机制。比如重试3次,前两次从池中获取连接执行,如果报的错是失效的连接等有关连接问题的错误,那么第3次从池中获取的时候带上参数,指定获取新建的连接,同时连接池移除前两次获取的失效的连接。

池满的处理机制

连接池不可能无限的容纳连接,当池满时,有两种处理机制:

1、池新建连接,并返回给客户端,当客户端用完时,如果池满则关闭连接,否则放入池中。

2、设置一定的超时时间来等待空闲连接。需要客户端加入重试机制,避免因超时之后获取不到空闲连接产生的错误。

基本原理

  1. 服务启动时建立连接池。
  2. 初始化连接池,建立最大空闲连接数个连接。
  3. 请求到来时,从池中获取一个连接。如果没有空闲连接且连接数没有达到最大活跃连接数,则新建连接;如果达到最大活跃连接数,设置一定的超时时间,等待获取空闲连接。
  4. 获取到连接后进行通信服务。
  5. 释放连接,此时是将连接放回连接池,如果池满则关闭连接。
  6. 释放连接池,关闭所有连接。

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。
  • 不同场景对应的连接池配置也不一样,需要根据自己的场景压测得出连接池的最佳参数配置。