拆开 Hical:为什么不用每个连接一个 timer 协程?IdleScanner 集中式空闲扫描设计

[Hical] 为什么不用每个连接一个 timer 协程?IdleScanner 的集中式空闲扫描设计 本专栏文章:拆开 Hical · 第 5 篇 本篇讲一个大多数 HTTP 框架都有的、但很少被拿出来单独聊的功能:空闲连接超时断开。 功能本身不复杂——如果一个 keep-alive 连接 60 秒没有新请求,就关掉它。但"怎么实现"有两种完全不同的路线,方案选择直接影响内存和 CPU 成本。 1. 方案 A:每连接一个 timer 协程(大多数框架的做法) 1 2 3 4 5 6 7 8 9 为每个连接创建一个 steady_timer,然后 co_await 在它上面: co_spawn(io_context, [self]() -> Awaitable<void> { steady_timer timer(60s); auto result = co_await (timer.async_wait() || socket.async_read(...)); // 如果 timer 先到 → 超时关闭 // 如果 socket 先到 → 取消 timer,处理请求 }); 问题在哪? ...

August 10, 2026 · 4 min · 750 words

定时器系统(Timer + TimerQueue + TimingWheel)

第 8 课:定时器系统(Timer + TimerQueue + TimingWheel) 对应源文件: trantor/net/inner/Timer.h/.cc — 单个定时器对象 trantor/net/inner/TimerQueue.h/.cc — 定时器优先队列(最小堆) trantor/utils/TimingWheel.h/.cc — 分级时间轮(高并发连接超时) 一、三个类的分工 1 2 3 4 5 6 Timer — 单个定时器的数据:到期时间、回调、是否重复 │ TimerQueue — 管理所有定时器,最小堆,驱动到期回调 │ TimingWheel — 用于大量连接的超时检测,O(1) 插入/删除 基于 TimerQueue 的 runEvery 驱动 两套定时器,用途不同: TimerQueue:精确定时,适合少量定时任务(heartbeat 广播、延迟关闭等) TimingWheel:粗粒度超时,适合万级连接的空闲超时检测 二、Timer:单个定时器 2.1 数据成员 1 2 3 4 5 6 TimerCallback callback_; // 到期执行的函数 TimePoint when_; // 到期时间(steady_clock,单调时钟) TimeInterval interval_; // 重复间隔(microseconds,为 0 表示一次性) bool repeat_; // = (interval_.count() > 0) TimerId id_; // 全局唯一 ID(原子递增) static std::atomic<TimerId> timersCreated_; // 全局计数器 2.2 ID 生成 1 2 3 4 5 // Timer.cc 第 21 行 std::atomic<TimerId> Timer::timersCreated_ = ATOMIC_VAR_INIT(InvalidTimerId); // 初始值 0 // 构造时(Timer.cc 第 29 行) id_(++timersCreated_) // 原子前自增,从 1 开始 每个 Timer 对象构造时自动分配唯一 ID,多线程安全,不需要锁。InvalidTimerId = 0 作为哨兵值。 ...

March 18, 2025 · 10 min · 2008 words