引言
- 下列代码模仿一段RPC请求的执行过程,执行后会有哪些问题:
RPC代码示例
- 答案:因为超时控制后未阻断后续请求,导致并发读写产生Panic
- 思考:客户端发起 HTTP 请求后,如果在指定时间内没有收到服务器的响应,则自动断开连接,超时控制是如何工作的?
什么是时间轮
- 思考:一定有一个类似于定时器的工具在执行,到时间后,中断任务。那么这个定时器是什么样的数据结构?又是如何实现这个定时功能的?
- 理论上
- 客户端发起请求后,立即创建(启动)一个 Timer:到期间隔为 d,到期后执行 “断开连接” 的操作。
- 如果到期间隔 d 以内收到了服务器的响应,客户端就删除(停止)这个 Timer。
- 如果一直没有收到响应,则 Timer 最终会到期,然后执行 “断开连接” 的操作。
- 实际上
- 现代的 Web 服务动辄管理 100w+ 的连接,每个连接都会有很多超时任务(比如发送超时、心跳检测等),如果每个超时任务都对应一个 Timer,性能会比较低下
- 破解之法:采用时间轮实现的 Timer来管理连接任务,使得创建和删除连接任务的时间复杂度为 O(1)
时间轮种类和设计思路
- 常见的时间轮实现有两种:
- 简单时间轮(Simple Timing Wheel)—— 比如 Netty4 的 HashedWheelTimer
- 层级时间轮(Hierarchical Timing Wheels)—— 比如 Kafka 的 Purgatory
简单时间轮
简单时间轮的设计思路
- 一个 简单时间轮就是一个循环列表,列表中的每一格包含一个定时任务列表(双向链表)。一个时间单位为 u、大小为 n 的简单时间轮,可以包含的定时任务的最大到期间隔为 n*u。
- 以 u 为 1ms、n 为 3 的简单时间轮为例,可以包含的定时任务的最大到期间隔为 3ms
简单时间轮示例

简单时间轮的实现
index := 0
timingWheels := make([]interface{
}, 10)
for {
time.Sleep(1 * time.Second)
index = index %