nodejs源码_记一次nodejs问题排查

相信大家都遇到过Error: read ECONNRESET这个错误,本文分享针对该错误的分析过程。虽然通过ECONNRESET错误码我们很容易查到这个错误意味着什么,但是通过源码和分析工具进行一次彻底的分析,会让你更加了解这个错误的产生和原理。更让人神清气爽。
本文分为两个部分,首先通过nodejs源码分析这个错误产生的原因,然后通过网络工具抓包的方式捕获这个错误。
1 源码分析
我们从建立一个tcp连接成功后,nodejs执行的操作开始分析(net.js)。

c8af7339f3caffdf53bdb3b0cb0b9ae2.png

这是连接成功后执行的nodejs回调。回调里执行了新建一个socket表示和客户端通信的对象。我们看new Socket做了什么事情。

3240eba02573ec1dbc70171b81cff790.png

new Socket的主要逻辑有

1 保存和客户端通信的handle(socket)

2 注册读回调

3 注册读事件

我们先看第三点

cdeaf1d60f0e775733c523d02873d6f0.png

socket是可读可写的流,read(0)直接调用可读流的函数,可读流提供了抽象的逻辑,具体的读取操作由子类实现(实现_read函数,read会调用_read函数)。我们看一下Socket类_read函数的实现。

7289a00258048df256bf3809c8fd35fa.png

直接调用handle的readStart函数。因为我们这里使用的是tcp服务。所以handle对应的实现在http://tcp_wrap.cc里。但是我们发现http://tcp_wrap.cc没有readStart函数。一路往父类找,最终在http://stream_wrap.cc找到了该函数。

31bd930b5dbe00a1db000f13fe0e0503.png

该函数直接调用libuv的uv_read_start函数,三个入参分别是

1 uv_tcp_t结构体

2 分配内存保存读取的数据

3 读取后执行的回调(包括读取失败)

继续往下走。

ae52933c21c19ef78d265da3a638688f.png

这时候nodejs就在底层注册了一个可读事件,等到有数据或者发送出错的时候,会触发上层回调(虽然只注册了可读事件,但是如果有错误发生,epoll会返回POLLIN和POLLERR事件)。这时候客户端发送了一个rst。这时候会执行libuv的回调uv__stream_io(而不是nodejs传进来那个,那个read_cb,read_cb是由libuv回调的)

5f79b3be8c6ae7f2cea250e516c1e02a.png

接着我们看uv_read

8edfe2a4cb25f1cac7e2b38485b21be4.png

重点在read函数,我们不妨多看点代码,看一下rst和read在linux下的实现。

7318ac067d1d660a0dbe1960f5a694e0.png

上面是操作系统收到一个rst包时的操作。设置对应socket的错误信息为ECONNRESET,并设置状态为close。如果这时候用户执行read会怎样呢?

ceda24fe99c34180250fece17bfaed5b.png

read函数会直接把错误信息返回给调用方。我们回到libuv中,当libuv调用read函数的时候,返回了错误码ECONNRESET。然后libuv执行nodejs的read_cb回调。如果我们还记得的话,nodejs提供的回调是OnUvRead。

298cb0f5a5868f36c129d6452a2f4adb.png

nodejs套了很多层,不过我们还是找到了他,最后的MakeCallback(env->onread_string(), arraysize(argv), argv)就是执行js层的onread函数。这个函数我们一开始的时候也提到了。回到net.js。

423f0c7c85e9f5de7734257dbf750a06.png

nodejs的onread函数执行了destroy函数。这里就不具体展开,destroy做的事情就是调用_destroy函数。然后emit一个error事件,并传入一个Error对象(包含了错误码和系统调用函数等信息)。触发error事件的时候,我们就输出了read ECONNRESET。至此,整个源码分析过程结束。

2 抓包分析
登录服务器,使用tcpdump工具,主要是过滤出想要的数据包。这里找出有问题的那几个ip。过滤条件设置为

tcpdump -i any -q -A  -nn src ip1 or dst ip1 or src ip2 or dst ip2 -w tcp.cap

保存为cap文件,然后下载到wireshark分析(linux下分析会比较麻烦点)。最后发现同一个时间点,抓包和日志系统都输出了相关的错误。

bb4694de2df6f9fb7c0d4d93d5f50f9b.png

608da063bf63295b7a504eb23429257f.png

总结:本文分享了通过源码分析和实践的方式排查nodejs问题,通过源码我们能更了解问题的原因,通过工具可以验证我们的想法。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值