在看 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的关系。
上篇中提到用户在请求服务时候可能会遇到socket hang up
与connect ECONNREFUSED
等问题以及timer作为资源保护
的措施,那本篇将描述server端与client端超时那点事儿。
超时参数
在CS架构
中,大概有三处接口
用户可以通过编程来设置网络
相关timeout的入口。
-
Nodejs层面: server,client
-
操作系统层面: socket
Server端
const server = http.createServer();
server.timeout(60000)
复制代码
Client端
const request= https.request();
request.setTimeout(60000);
复制代码
socket读写(socket还有connect超时,只是未直接暴露)
setsockopt(int s, int level, int optname, const void *optval, socklen_t optlen);
// 可以通过 optname = SO_RCVTIMEO | SO_SNDTIMEO 来设置 socket发送和接受数据的最大时间,如果不设置
复制代码
三者关系
无论是Server还是Clinet都是通过系统调用socket
来实现网络通信的,最直观感受
是server和client端的timeout参数传递到socket系统调用上,那么timeout最终转化为setsocketopt的参数了吗?这个问题可以从两个方向分别来确认: bindings层
和internal module层
。
bindings 层
Nodejs是借助libuv来实现网络通信
的,确认这个问题的最简单办法是查看bindings层有无调用libuv的相关方法。在libuv中暴露uv__socket_sockopt
方法来设置socket超时,然后搜索在node/src
源码目录下搜索uv__socket_sockopt,并无任何调用,所以三者是清白的
。
internal module层
从server端依次追踪timeout参数调用链为: http -> _http_server -> net -> stream_base_common -> timer.setUnrefTimeout
。
从client端依次追踪timeout参数调用链为: http -> _http_client -> net -> stream_base_common -> timer.setUnrefTimeout
。
可以发现双方均使用了nodejs本身的定时器,与setsocket无关,所以三者是清白的
。
不清不白
由上面可知,三者的timeout是清白的,互不污染
的,但三者又是互相关联
的,互相影响
的,要搞清楚三方关系,必须先弄清楚三个代表
: socket,request,response。
以server端为例: socket代表client和server的一次connection
;request代表本次connection
的client的请求;response代表本次connection
的server响应。
- 三者均可以
分别注册各自
的 timeout - socket 的timeout回调会
触发timeout事件
,从而使得req和res触发timeout
明明白白
由上可知,socket的timeout在整个过程中起到了关键的作用,所以有必要搞清楚socket的timeout流程
逆时针流转
灰色线代表listen的过程,本过程主要是注册 TCP handler到libuv的事件循环
中;
紫色线代表一次connection的过程,本过程主要是注入socket到回调中并给socket注册回调函数,回调函数用来销毁该socket
。
整个流程串起来花了很久,主要卡在socket的来源,后来发现是在libuv中注入的,所以特别提示下:
src/connection_wrap.cc
下的OnConnection
设与不设
Timer作为资源保护的一种手段在广泛应用,但是没有免费的午餐,timer是有代价的
,设与不设,设多少?
Nodejs编程中需要时刻留意垃圾回收
问题。如果设置了timer则会在js侧有大量的队列节点需要进行垃圾回收扫描;libuv和os层也维护了一些定时相关的数据结构;过多的定时中断也会代码一些性能开销(弱,js只有低精度定时器)。
Nodejs中的server默认是不设置,所以socket是长链接,长链接会带来一些安全性问题
。
因地制宜
,根据不同的应用场景设置不同的策略。例如,内部系统
可以考虑不设置,或者设置较长的timeout(nodejs服务通常不会直接暴露,往往有多个前置层,timeout参数可以设置到前置层,例如nginx);外部系统
需要设置时间相对短的timeout。
变态
到此关于定时器的一点总结算是告一段落,原本期望一周写完,但是一不留神,timeout了。可见,timeout是常态,不timeout才是变态
。
关注我的微信公众号,欢迎留言讨论,我会尽可能回复,感谢您的阅读。