溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

Node.js中處理Accept時(shí)出現(xiàn)Emfile的解決方法

發(fā)布時(shí)間:2021-06-16 09:56:28 來(lái)源:億速云 閱讀:177 作者:chen 欄目:web開(kāi)發(fā)

這篇文章主要講解了“Node.js中處理Accept時(shí)出現(xiàn)Emfile的解決方法”,文中的講解內(nèi)容簡(jiǎn)單清晰,易于學(xué)習(xí)與理解,下面請(qǐng)大家跟著小編的思路慢慢深入,一起來(lái)研究和學(xué)習(xí)“Node.js中處理Accept時(shí)出現(xiàn)Emfile的解決方法”吧!

EMFILE表示進(jìn)程打開(kāi)的文件描述符達(dá)到了上限,比如建立了一個(gè)TCP連接后,調(diào)用accept函數(shù)的時(shí)候就可能觸發(fā)這個(gè)錯(cuò)誤。那么這個(gè)會(huì)導(dǎo)致什么問(wèn)題呢?首先我們看看Node.js是如何處理連接的。

void uv__server_io(uv_loop_t* loop, uv__io_t* w, unsigned int events) {

uv_stream_t* stream;

int err;

stream = container_of(w, uv_stream_t, io_watcher);

while (uv__stream_fd(stream) != -1) {

// 摘取一個(gè)TCP連接

err = uv__accept(uv__stream_fd(stream));

// 記錄下來(lái)

stream->accepted_fd = err;

// 執(zhí)行上層回調(diào),回調(diào)里消費(fèi)accepted_fd

stream->connection_cb(stream, 0);

// 下一個(gè)循環(huán)

}

}

當(dāng)監(jiān)聽(tīng)socket上可讀事件觸發(fā)的時(shí)候,Node.js就會(huì)執(zhí)行uv__server_io進(jìn)行處理。在uv__server_io中Node.js就會(huì)不斷地調(diào)用accept摘取連接,然后執(zhí)行回調(diào)處理該連接。這是正常的流程,那么如果accept出錯(cuò)了,那會(huì)怎么樣?比如返回了EMFILE錯(cuò)誤。

因?yàn)镹ode.js中,epoll的工作模式是水平觸發(fā),所以每輪事件循環(huán)中,uv__server_io都會(huì)被觸發(fā),然后執(zhí)行accept,接著觸發(fā)錯(cuò)誤(如果還沒(méi)有可用的文件描述符的話)。然而底層已完成三次握手的TCP連接無(wú)法得到處理,客戶端也只能默默地在等待。Node.js選擇的處理策略是關(guān)閉連接來(lái)通知客戶端,服務(wù)器已經(jīng)過(guò)載。我們看看Node.js具體是怎么做的。在初始化第一個(gè)Libuv  stream的時(shí)候會(huì)首先預(yù)留一個(gè)文件描述符。

if (loop->emfile_fd == -1) {

err = uv__open_cloexec("/dev/null", O_RDONLY);

if (err < 0)

/* In the rare case that "/dev/null" isn't mounted open "/"

* instead.

*/

err = uv__open_cloexec("/", O_RDONLY);

if (err >= 0)

loop->emfile_fd = err;

}

我們看到Node.js打開(kāi)了一個(gè)資源,然后拿到了一個(gè)文件描述符保存到emfile_fd。當(dāng)Node.js處理TCP連接的時(shí)候,這個(gè)emfile_fd可能就會(huì)被用上。

// 摘取TCP連接

err = uv__accept(uv__stream_fd(stream));

if (err < 0) {

// 文件描述符過(guò)載

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;

}

我們看到當(dāng)uv_accept返回UV_EMFILE錯(cuò)誤的時(shí)候,會(huì)執(zhí)行uv__emfile_trick。

static int uv__emfile_trick(uv_loop_t* loop, int accept_fd) {

int err;

int emfile_fd;

if (loop->emfile_fd == -1)

return UV_EMFILE;

// 關(guān)閉預(yù)留的文件描述符,下面的uv_accept才能執(zhí)行成果

uv__close(loop->emfile_fd);

loop->emfile_fd = -1;

// 循環(huán)關(guān)閉無(wú)法處理的TCP連接

do {

// 摘取TCP連接

err = uv__accept(accept_fd);

if (err >= 0)

// 關(guān)閉TCP連接,通知客戶端服務(wù)器過(guò)載

uv__close(err);

} while (err >= 0 || err == UV_EINTR);

// 重新獲取一個(gè)預(yù)留的文件描述符

emfile_fd = uv__open_cloexec("/", O_RDONLY);

if (emfile_fd >= 0)

loop->emfile_fd = emfile_fd;

return err;

}

我們看到uv__emfile_trick中關(guān)閉了所有無(wú)法處理的TCP連接,然后重新補(bǔ)充預(yù)留的文件描述符。正常來(lái)說(shuō)uv_accept最后會(huì)返回UV_EAGAIN表示沒(méi)有連接需要處理了,從而結(jié)束處理連接的整個(gè)邏輯。

感謝各位的閱讀,以上就是“Node.js中處理Accept時(shí)出現(xiàn)Emfile的解決方法”的內(nèi)容了,經(jīng)過(guò)本文的學(xué)習(xí)后,相信大家對(duì)Node.js中處理Accept時(shí)出現(xiàn)Emfile的解決方法這一問(wèn)題有了更深刻的體會(huì),具體使用情況還需要大家實(shí)踐驗(yàn)證。這里是億速云,小編將為大家推送更多相關(guān)知識(shí)點(diǎn)的文章,歡迎關(guān)注!

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI