在看 Node http 模块文档的时候, 才留意到
server.timeout这个属性, 本想简单介绍一下, 但是在梳理过后发现关于timeout有庞大的内容支撑:server.timout -> node core timers -> uv timers -> linux msleep/hrtimer -> clocksource -> tsc -> cmos rtc -> clock cycle, 所以拆分成几部分大致做下介绍, 期望定时器系列结束之后, noder 能够大致明白:clock cycle是如何驱动 linux 的msleep/hrtimer; linux 的 timers 与 uv timers 的关系; node timers 与 uv timers的关系。
前面叙述了计算机中timer实现原理,libuv层如何借助epoll_wait实现timer的机制,现在讲下Nodejs应用层相关的timer。
数据结构与调用关系
从上之下各层的数据结构有:
- Nodejs - 链表,最小堆(实现的优先队列)
- Libuv - 最小堆
- Linux - 分桶-链表,红黑树
考虑一下场景,三者之间的数据模型是怎样的
setTimout(fn1(){}, 1000);
setTimout(fn2(){}, 1000);
setTimout(fn3(){}, 2000);
setTimout(fn4(){}, 2000);
setTimout(fn5(){}, 3000);
setTimout(fn6(){}, 3000);

关键函数:
scheduleTimer- node 调用此函数构建了libuv的数据processTimers- node 定时器的回调函数,libuv 到时会触发次回调,有此可见具体的fn[x]并没有传递而libuv,而是在node端执行的- epoll_wait - libuv 调用linux系统函数构建了高精度定时器的红黑树
关键点:
- 三个层面的数据并不一样
- Node端
时间+函数,三个时间点,6个函数 - Libuv端
时间,三个时间点 - Linux端
最小时间,一个时间点
- Node端
- 调用关系
- Nodejs 把
固定的定时回调函数processTimers传递给libuv并通过scheduleTimer调用libuv,等待回调 - Libuv 读取最小时间
同步阻塞调用Linux的epoll_wait,进程进入可中断睡眠,等待中断 - Linux 高精度定时器设置APIC的cycles,
继续执行其他任务,APIC到时触发中断
- Nodejs 把
Nodejs 的 timers们
Nodejs 提供的timers们,除了给用户使用,其实自身也在大量使用。对于Node来说,timer应该算是一种保护机制,先看下Node自身哪里在用吧。
TCP 相关模块- http[s],net- server.timeout
- server.keepAliveTimeout tcp
child_process 模块- child_process.exec(..., {timeout})
dns 模块- Resolver({ timeout })
readline 模块- readline.createInterface({..., escapeCodeTimeout})
vm 模块- script.runInContext(..., { timeout})
以上模块或模块中的方法都是系统资源的重度消耗者,而nodejs 作为server端的编程语言,需要特别留意资源的开销,所以在有资源开销地方一般都会Timer守护者,以便及时的释放资源。
server.timeout
本次关于定时器的主题,来自网上关于socket hang up与connect ECONNREFUSED的讨论,至此还没有涉及这方面内容。此内容将会涉及到timer以外的,socket, tcp等层面的内容,该内容相对独立,所以会作为定时器最后一节,放到下次讨论。
关注我的微信公众号"SUNTOPO WLOG",欢迎留言讨论,我会尽可能回复,感谢您的阅读。