定时器: server timeout

在看 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 upconnect 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响应。

server_timer1.png

  1. 三者均可以分别注册各自的 timeout
  2. socket 的timeout回调会触发timeout事件,从而使得req和res触发timeout

明明白白

由上可知,socket的timeout在整个过程中起到了关键的作用,所以有必要搞清楚socket的timeout流程

逆时针流转

servver_timer2.webp

灰色线代表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才是变态

关注我的微信公众号,欢迎留言讨论,我会尽可能回复,感谢您的阅读。

© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享