当前位置: 首页 > 科技观察

Node.js中关于Accept时Emfile的处理

时间:2023-03-19 01:16:58 科技观察

Node.js中EmfileonAccept的处理转载本文请联系编程杂技公众号。EMFILE表示进程打开的文件描述符已达到上限。例如建立TCP连接后,调用accept函数时可能会触发该错误。那么这会造成什么问题呢?首先,让我们看看Node.js是如何处理连接的。voiduv__server_io(uv_loop_t*loop,uv__io_t*w,unsignedintevents){uv_stream_t*stream;interr;stream=container_of(w,uv_stream_t,io_watcher);while(uv__stream_fd(stream)!=-1){//提取ATCPconnectionerr=uv__accept(uv__stream_fd(stream));//记录下来stream->accepted_fd=err;//执行上层回调,并消费accepted_fdstream->connection_cb(stream,0);//下一个循环}}当监听套接字上的readable事件被触发时,Node.js会执行uv__server_io进行处理。在uv__server_io中,Node.js会不断的调用accept来获取连接,然后执行回调处理连接。这是一个正常的过程,那么accept出现错误怎么办?例如,返回EMFILE错误。因为在Node.js中,epoll的工作方式是水平触发,所以在每一轮事件循环中,都会触发uv__server_io,然后执行accept,然后触发错误(如果没有可用的文件描述符)。但是已经完成三次握手的底层TCP连接无法处理,客户端只能静静等待。Node.js选择的处理策略是关闭连接来通知客户端服务器超载。让我们看看Node.js是如何做到的。在初始化第一个Libuv流时,会先保留一个文件描述符。if(loop->emfile_fd==-1){err=uv__open_cloexec("/dev/null",O_RDONLY);if(err<0)/*在极少数情况下,“/dev/null”未安装打开“/”*。*/err=uv__open_cloexec("/",O_RDONLY);if(err>=0)loop->emfile_fd=err;}我们看到Node.js打开了一个资源,得到了一个文件描述符,保存到emfile_fd中。当Node.js处理TCP连接时,可能会用到这个emfile_fd。//提取TCP连接err=uv__accept(uv__stream_fd(stream));if(err<0){//文件描述符重载if(err==UV_EMFILE||err==UV_ENFILE){err=uv__emfile_trick(loop,uv__stream_fd(stream));if(err==UV_EAGAIN||err==UV__ERR(EWOULDBLOCK))break;}stream->connection_cb(stream,err);continue;}我们看到uv_accept返回UV_EMFILE错误时,会执行uv__emfile_trick。staticintuv__emfile_trick(uv_loop_t*loop,intaccept_fd){interr;intemfile_fd;if(loop->emfile_fd==-1)returnUV_EMFILE;//关闭保留的文件描述符,下面的uv_accept可以执行结果uv__close(loop->emfile_fd);loop->emfile_fd=-1;//循环关闭未处理的TCP连接do{//提取TCP连接err=uv__accept(accept_fd);if(err>=0)//关闭TCP连接,通知客户端服务器过载uv__close(err);}while(err>=0||err==UV_EINTR);//重新获取保留的文件描述符emfile_fd=uv__open_cloexec("/",O_RDONLY);if(emfile_fd>=0)loop->emfile_fd=emfile_fd;returnerr;}我们看到uv__emfile_trick关闭了所有无法处理的TCP连接,然后补充保留的文件描述符。通常情况下,uv_accept最后会返回UV_EAGAIN,表示没有连接需要处理,从而结束整个处理连接的逻辑。