写点什么

Aeron 是如何实现的?—— Ipc 异常情况处理

作者:BUG侦探
  • 2021 年 12 月 14 日
  • 本文字数:2380 字

    阅读完需:约 8 分钟

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 之间是通过心跳超时判断对方是否异常。

  1. Client 周期性检查 Driver 是否超过 driverTimeoutMs 没有心跳。

  2. 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 的检查分两步:

  1. 如果心跳时间超过 driverTimeoutMs(也就是 Client 认为 Driver 异常了),那么抛出 DriverTimeoutException,这是个不可恢复异常,默认会导致程序退出。

  2. 否则再检查 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 个:

  1. clients:客户端

  2. publicationLinks:pub 的关系

  3. subscriptionLinks:sub 的关系

  4. 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 状态。主要处理了以下几个典型情况:

  1. Active 状态,并发写入的情况如果某个 Client 写了一半异常退出了(此处用了 Blocked 这个词,判断阻塞的默认时间是 15 秒),那么会造成脏数据,订阅端会一直卡住,那么在 checkForBlockedPublisher 这个方法里处理。

  2. DRAINING 状态此时没有 pub 关系了,那么处理的主要逻辑是,等待所有的 sub 关系都消费完,然后通知 ON_UNAVAILABLE_IMAGE,进入 LINGER 状态。

  3. LINGER 状态清理清理所有 sub 关系,标识该 pub 实体可以关闭。

关闭的逻辑就比较简单了







发布于: 2 小时前阅读数: 5
用户头像

BUG侦探

关注

还未添加个人签名 2021.06.08 加入

专注于发掘程序员/工程师的有趣灵魂,对工作中的思路与总结进行闪光播报。

评论

发布
暂无评论
Aeron 是如何实现的?—— Ipc 异常情况处理