在dble中每个后端MySQL节点由 PhysicalDbInstance 表示,PhysicalDbInstance中维护了两类连接:
- 大部分业务使用的连接由连接池管理
- 独立于连接池之外的连接,这类连接主要有两类:MySQL实例的心跳连接和用于OneTimeJob的一次性任务的连接,这种连接是一次性的,用完即关,次数也比较少。
后端连接池使用 CopyOnWriteArrayList 存储该MySQL实例的全量连接,通过连接的state状态来维护连接的初始化,借出,空闲,心跳,移除状态。连接池初始化之后会维护一个evictor线程来维持连接池的扩缩容以及空闲连接的有效性。结构如下图所示:
业务向连接池请求获取后端连接,会遍历连接池中的全量连接直到找到第一个空闲连接。若连接池中当时没有空闲连接,则线程会进入超时等待队列,在超时时间内未获取到连接,前端报错。
后端连接处理完业务之后,状态会被置为空闲,并且处理线程会唤醒在超时等待队列中的线程来重新获取连接。
连接池初始化之后会维护一个evictor线程来维持连接池的扩缩容以及空闲连接的有效性,evictor线程是一个定时任务。
扩容:当空闲连接数小于minCon时,维持连接池中的空闲连接在minCon的数量上,每次扩容的连接数量通过以下公式计算:min(配置的最小空闲连接数 - 当前连接池中空闲连接数, 配置的最大连接数 - 连接池中的总连接数) - 正在创建的连接数,若数量大于0,则创建该数量的连接。
缩容:当空闲连接数大于minCon时,维持连接池中的空闲连接在minCon的数量上,每次关闭的连接数量通过以下公式计算:(连接池中的最小连接数 - 配置的最小连接) > 0 && 连接达到 idleTimeout。
在连接的不同阶段,提供对连接有效性的检测手段。
- testOnCreate为true,在连接被创建后,会发送ping命令探测连接有效性,若在connectionHeartbeatTimeout没有收到结果,会关闭连接。
- testOnBorrow为true,在连接被借出后,会发送ping命令探测连接有效性,若在connectionHeartbeatTimeout没有收到结果,会关闭连接。
- testOnReturn为true,在连接被返回后,会发送ping命令探测连接有效性,若在connectionHeartbeatTimeout没有收到结果,会关闭连接。
- testWhileIdle为true,对所有空闲连接,发送ping命令探测连接有效性,若在connectionHeartbeatTimeout没有收到结果,会关闭连接。
由上文我们知道,后端连接池使用 CopyOnWriteArrayList 存储该MySQL实例的全量连接,通过连接的state状态来维护连接的初始化,借出,空闲,心跳,移除状态。下面是连接状态的跃迁图:
dble对分库分表的流量有控制功能,详见2.25 dble流量控制。
后端连接的高水位(flowHighLevel)和低水位(flowLowLevel)的配置也在这里体现,单位字节
属性名 | 默认值 | 单位 | 含义 |
---|---|---|---|
testOnCreate | false | 无 | 连接创建后是否检测有效性 |
testOnBorrow | false | 无 | 连接被借出后是否检测有效性 |
testOnReturn | false | 无 | 连接被返回时是否检测有效性 |
testWhileIdle | false | 无 | 连接空闲时是否检测有效性 |
connectionTimeout | 30000 (30s) | 毫秒 | 获取连接的超时时间 |
connectionHeartbeatTimeout | 20 | 毫秒 | 空闲连接检测后的超时时间 |
timeBetweenEvictionRunsMillis | 30000 (30s) | 毫秒 | 扩缩容线程的检测周期 |
idleTimeout | 600000 (10 minute) | 毫秒 | 连接空闲多久之后被回收 |
heartbeatPeriodMillis | 10000 (10s) | 毫秒 | 连接池的心跳周期 |
evictorShutdownTimeoutMillis | 10000 (10s) | 毫秒 | 扩缩容线程停止的超时时间 |
flowHighLevel | 4194304 | 字节 | 后端连接流量控制的高水位 |
flowLowLevel | 262144 | 字节 | 后端连接流量控制的低水位 |
示例:
<?xml version="1.0"?>
<!DOCTYPE dble:db SYSTEM "db.dtd">
<dble:db xmlns:dble="http://dble.cloud/">
<dbGroup name="dbGroup1" rwSplitMode="1" delayThreshold="100">
<heartbeat errorRetryCount="1" timeout="10">show slave status</heartbeat>
<dbInstance name="instanceM1" url="ip4:3306" user="your_user" password="your_psw" maxCon="200" minCon="50" primary="true">
<property name="testOnCreate">false</property>
<property name="testOnBorrow">false</property>
<property name="testOnReturn">false</property>
<property name="testWhileIdle">true</property>
<property name="connectionTimeout">30000</property>
<property name="connectionHeartbeatTimeout">20</property>
<property name="timeBetweenEvictionRunsMillis">30000</property>
<property name="idleTimeout">600000</property>
<property name="heartbeatPeriodMillis">10000</property>
<property name="evictorShutdownTimeoutMillis">10000</property>
<property name="flowHighLevel">4194304 </property>
<property name="flowLowLevel">262144 </property>
</dbInstance>
<!-- can have multi read instances -->
<dbInstance name="instanceS1" url="ip5:3306" user="your_user" password="your_psw" maxCon="200" minCon="50" primary="false">
<property name="heartbeatPeriodMillis">60000</property>
</dbInstance>
</dbGroup>
</dble:db>
dble后端MySQL节点的心跳管理是通过定时任务来完成的,检测周期由heartbeatPeriodMillis来控制。dble会对每一个后端MySQL节点持有一个长连接,定期发送心跳语句,根据返回结果的不同将连接池标记为不同的状态。若心跳异常,会影响evictor线程的扩缩容。
我们可以将每个心跳周期简单划分为两个阶段:
- 检测阶段:心跳检测发起到收到回复的阶段
- 空闲阶段:心跳返回后到下一个心跳检测发起的阶段
dble的心跳状态有四种:
- init状态:初始状态,具体指收到第一个心跳响应报文前的状态
- ok状态:收到一次正常的心跳返回后的状态
- timeout状态:最近的一次心跳在HeartbeatTimeout时间段内没有收到响应
- error状态:心跳语句返回错误或者心跳连接异常都可能导致此种状态,dble里面会有重试机制来预防网络抖动等网络方面的异常。
此处的心跳重试分为三种情况:第一种是心跳语句返回错误导致的重试,第二种是心跳连接关闭导致的重试,第三种是心跳超时后才收到响应导致的重试。
- 对于心跳语句返回失败,dble会立即将连接池状态置为error状态,随即会发送errorRetryCount次心跳,若有一次心跳正常,心跳恢复成ok状态。
- 对于心跳连接关闭引起的失败,dble会在接下来的时间立即发送errorRetryCount次心跳,若有一次心跳正常,则停止重试,但如果都失败,则将连接池状态置为error状态
- 对于标记为timeout状态后收到姗姗来迟的OK响应, 则将会被重置为init状态并立即发送一次心跳。