尝试为nodejs贡献代码
发布于 4 年前 作者 theanarkh 4514 次浏览 来自 分享

1 需求背景

一直都有在看一些开源项目的代码,但是还没有试过提交pr。因为最近在研究websocket和keep-alive。而websocket涉及到长连接,过多无用的长连接对系统来说是负担,是否可以尽快发现对端是否已经掉线,从而释放这个连接来减少系统压力呢,就这个初衷,想通过wireshark和nodejs调试一下心跳机制,但是发现nodejs对这个的支持不是很好。tcp的心跳机制,支持三个配置,但是nodejs的setKeepAlive只支持一个配置(后面发现最新版代码里有一点支持的痕迹了,但是没有给用户提供接口),所以就产生了提交pr的想法。代码改动不大,但是整个流程走下来,也挺费时间的。     本文大致分享一下这个过程。我的诉求是想让nodejs把修改心跳机制和相关配置的接口暴露给用户。但是libuv层的接口本身就不支持这个能力。所以要解决这个问题,要修改c、c++、js的代码。因为nodejs的架构就是这样,libuv提供能力,c++套壳,js调用。所以你想加一个libuv不支持的功能时,你就得从libuv改起。

2 技术背景

tcp连接一旦建立,默认是不会断开的,但是操作系统支持心跳机制,只不过默认是关闭的,心跳机制有几个配置,分别是是否开启,多久没有收到数据或ack后开启发送第一个心跳包,隔多久发送一个,发送多少个后认为连接断开。但是有一个问题是,心跳机制并不是什么时候都好使,这大概源于tcp协议的复杂性。如果两端都没有数据来往时,心跳机制能很好地工作,但是一旦本端有数据发送的时候,他就会抑制心跳机制。我们看一下linux内核5.7.7的一段相关代码。 上面这一段是心跳机制中,定时器超时时,执行的一段逻辑,我们只需要关注红色框里的代码。一般来说,心跳定时器超时,操作系统会发送一个新的心跳包,但是如果发送队列里还有数据没有发送,那么操作系统会优先发送。或者发送出去的没有ack,也会优先触发重传。这时候心跳机制就失效了。所以这里除了想让nodejs支持keep-alive的配置外。还加入了linux的另一个属性TCP_USER_TIMEOUT。这个属性的功能是,在多久没有收到ack后,操作系统就认为这个连接断开了。看一下相关代码。 设置阈值 这是设置阈值的代码。 这是超时时判断是否断开连接的代码。我们看到有两个情况下操作系统会认为连接断开了。 1 设置了TCP_USER_TIMEOUT时,如果发送包数量大于1并且当前时间具体上次收到包的时间间隔已经达到阈值。 2 没有设置TCP_USER_TIMEOUT,但是心跳包发送数量达到阈值。 所以我们可以同时设置这两个属性。保证心跳机制可以正常运行。

3 开始写代码

有了诉求,那就开启写代码。首先到nodejs仓库fork一份代码出来,然后按照nodejs官方给的流程,最后提交pr。第一次提交pr的时候,reviewer建议我使用新接口的方式修改这个代码,因为我是修改setKeepAlive相关的代码,然后做了兼容处理。后面发现reviewer给的建议比较好,修改存量接口存在风险,兼容处理非常麻烦,提供新接口然用户切换会比较安全并且代码上也比较好写。整个过程比较麻烦的是,每次写完需要编译(尤其第一次),然后跑测试用例。这很费时间。还有一些代码风格的问题。另外libuv的修改是在libuv仓库,不是在nodejs仓库。

4 结果

下面是跑测试用例的漫长过程。 跑测试用例 下面是pr,还没review。 nodejs: https://github.com/nodejs/node/pull/34193 libuv: https://github.com/libuv/libuv/pull/2907

总结:第一次尝试参与开源,感觉很有趣,但是也比较累,整个流程走下来也挺费时间,不过为世界级软件贡献代码大概也是每个技术人的一个目标,不管怎样,也是一个很好的尝试。

回到顶部