Aeron 是如何实现的?—— Ipc 异常情况处理
接上文
Aeron 是什么?https://xie.infoq.cn/article/27422063a2cadcc054187135e
Aeron 中这么多空闲策略选哪个?https://xie.infoq.cn/article/41d0885f46594e90cbdba4b2b
Aeron 是如何实现的?—— Conductor https://xie.infoq.cn/article/b4953b06323cd26e3a1397874
Aeron 是如何实现的?—— Ipc Publication https://xie.infoq.cn/article/9c3c085daef8eb12d533d2f66
Aeron 是如何实现的?—— Ipc Subscription https://xie.infoq.cn/article/9bcc41516b0b4dc53a628302a
0. 简介
上文分析了 Ipc Subscription 的逻辑,本文继续分析一下 ipc 场景异常情况处理。
1. 两个心跳
总的来说,Client 与 Driver 之间是通过心跳超时判断对方是否异常。
Client 周期性检查 Driver 是否超过 driverTimeoutMs 没有心跳。
Driver 周期性检查 Client 是否超过 clientLivenessTimeoutMs 没有心跳。
Driver 没有心跳是个严重的问题,Client 默认将会自杀;Client 没有心跳,Driver 将会把相关的资源(pub 和 sub 关系等)关闭。下面详细看一下。
2. Client Conductor
2.1 命令处理超时 driverTimeoutNs
Client Conductor 向 Driver Conductor 发出的命令都有一个超时时间 driverTimeoutNs,如果超出了抛出 DriverTimeoutException,默认值 10_000 ms。
2.2 ClientConductor 的 AgentRunner 停顿时间 interServiceTimeoutNs
ClientConductor 的 AgentRunner 停顿时间大于 interServiceTimeoutNs,那么 Client 会抛出 ConductorServiceTimeoutException,并且设置 isTerminating = true,此时整个 Aeron 就不能用了,需要重新 Aeron.connect()。interServiceTimeoutNs 的值取的是 CncFileDescriptor.clientLivenessTimeoutNs(cncMetaDataBuffer),也就是 cnc.dat 中的 Client Liveness Timeout,默认 10 秒。
这是个“自觉性”很高的操作,因为超过这个 timeout,Driver 会认为 Client 异常,进行资源清理,此时 Client 主动进行“自我处理”,节省了一次异常交互。
2.3 keepAliveIntervalNs
与 interServiceTimeoutNs 相关的还有个 keepAliveIntervalNs,在数值上要求 keepAliveIntervalNs 小于 interServiceTimeoutNs,默认值为 500 ms 这个参数是 Client Conductor 更新自己心跳的周期,另外还检查 Driver Conductor 是否存活。Driver Conductor 的心跳时间放跟新在 cnc.dat 的 to-driver Buffer 尾部 consumerHeartbeatTime,Driver Conductor 的 AgentRunner 会定期更新这个心跳时间。Client 的检查分两步:
如果心跳时间超过 driverTimeoutMs(也就是 Client 认为 Driver 异常了),那么抛出 DriverTimeoutException,这是个不可恢复异常,默认会导致程序退出。
否则再检查 Counters Buffer 中的心跳计数器 heartbeatTimestamp,如果心跳计数器异常(也就是 Driver 认为 Client 超时,对计数器进行了清理),那么抛出 AeronException,设置 isTerminating = true;如果正常的话,就更新自己的心跳。
3. Publication & Subscription
在 Client 这里 Publication 和 Subscription 没有太多异常抛出。对于 Publication 的 offer,如果返回值为 CLOSED,表示这个通道已经关闭了,可能是 Driver 进行了资源清理,也可能是正常 close 了;如果返回 MAX_POSITION_EXCEEDED,也表示这个通道不能再用了,需要重新 add 一个。
还有个小细节,如果 offer 的数据大于 maxMessageLength,那么抛出 IllegalArgumentException,这需要在业务层面处理,Aeron 框架对消息大小就是有这个硬性限制。
对于 Subscription 的 poll,无论是没有可用的 Images,还是 Images 已经关闭了,都返回 0。对于 pub 或 sub 后 Client 崩溃退出,留下“脏”资源的异常情况,都在 Driver 中处理。
4. Driver Conductor
Driver Conductor 对异常情况检查的逻辑基本都在 processTimers 方法中:首先会更新 consumerHeartbeatTime,避免让 Client 认为 Driver 已经故障了,产生不可恢复的异常,对应上面 2.3 节的内容。然后检查各种资源,对于 ipc 场景有这 4 个:
clients:客户端
publicationLinks:pub 的关系
subscriptionLinks:sub 的关系
ipcPublications:pub 实体的封装,也就是 logbuffer 共享内存
资源检查的逻辑是通用的:
4.1 AeronClient 上文 2.3 节提到的 heartbeatTimestamp 如果超过 clientLivenessTimeoutMs 没有心跳更新,那么 Driver 就认为这个 AeronClient 失联了,进行资源关闭。
清理掉 heartbeatTimestamp 这个计数器,Client 下次更新心跳时就能识别出异常。
4.2 PublicationLink
检查与该 pub 关系对应的 AeronClient 是否超时,如果超时了,那么也关闭这个关系。
在 ipc 场景就是对关联的 pub 实体减一个引用,直到引用减为 0 才会对 pub 实体进行处理。
主要是设置状态为 DRAINING
4.3 IpcSubscriptionLink
检查与该 sub 关系对应的 AeronClient 是否超时,如果超时了,那么也关闭这个关系。
找到所有对应的 pub 实体,取消该 sub 关系。
更新清理相关的位置计数器,如果 sub 关系都关闭了,那么标识一下该 pub 实体没有连接了,Client 的 Publication 就能感知到。
4.4 IpcPublication
pub 实体的检查稍微复杂一些。
如果还有 pub 关系,那么处于 ACTIVE 状态,如果 pub 关系都关闭了,那么处于 DRAINING 状态。正常来说应该是 Active 状态,但是如果上面 3.2 的 PublicationLink 都关闭了,那么关联的 IpcPublication 就进入了 DRAINING 状态。主要处理了以下几个典型情况:
Active 状态,并发写入的情况如果某个 Client 写了一半异常退出了(此处用了 Blocked 这个词,判断阻塞的默认时间是 15 秒),那么会造成脏数据,订阅端会一直卡住,那么在 checkForBlockedPublisher 这个方法里处理。
DRAINING 状态此时没有 pub 关系了,那么处理的主要逻辑是,等待所有的 sub 关系都消费完,然后通知 ON_UNAVAILABLE_IMAGE,进入 LINGER 状态。
LINGER 状态清理清理所有 sub 关系,标识该 pub 实体可以关闭。
关闭的逻辑就比较简单了
版权声明: 本文为 InfoQ 作者【BUG侦探】的原创文章。
原文链接:【http://xie.infoq.cn/article/d56abda4fcccd5f05b5584f45】。文章转载请联系作者。
评论