在過去的日子裏,我一直在研究如何理解Node.js基於事件的風格如何處理比傳統的多線程方法更多的併發請求。最後,所有關於內存佔用和內存切換的內容都減少了,因爲Node.js只使用了幾個線程(V8單線程和一堆C++工作線程以及libuv的主線程)。在Node.js中扮演V8引擎的角色是什麼?
但是如何處理大量帶有幾個線程的請求,因爲最後一些線程必須被阻塞等待,例如數據庫讀取操作。 我認爲這個想法是:而不是同時阻塞客戶端線程和數據庫線程,只有數據庫線程被阻塞,並在客戶端線程結束時提醒客戶端線程。
這就是我理解Node.js的工作原理。
我一直在想,是什麼賦予Node.js處理HTTP請求的能力。 根據我閱讀到現在爲止,我的理解是libuv是誰做的工作:
把手代表能夠積極而執行某些操作 長壽命的對象。一些示例:當處於活動狀態時,準備句柄的每個循環迭代都會調用一次 回調,並且a TCP 服務器句柄在每次有新連接時都會調用它的連接回調。
所以,這是等待進入的HTTP請求的線程是libuv的主線程執行所述libuv事件循環。
所以,當我們在寫
const http = require('http');
const hostname = '127.0.0.1';
const port = 1337;
http.createServer((req, res) => {
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end('Hello World\n');
}).listen(port, hostname,() => {
console.log(`Server running at http://${hostname}:${port}/`);
});
...我在libuv投入,將在V8引擎,當請求中執行的回調?
的事件的順序將然後是
- 一個TCP數據包到達
- 操作系統創建一個事件,併發送至事件循環
- 事件循環處理該事件並創建一個V8事件
如果我在處理請求的匿名函數中執行阻塞代碼,我將阻塞V8線程。
爲了避免這種情況,我需要執行將在另一個線程中執行的非阻塞代碼。我想這「另一個線程」是libuv的主線程,其中
網絡I/O始終是在單個線程執行,每次循環的線程
這個線程不會阻止,因爲使用OS系統調用是異步的。
的epoll在Linux上,在OSX和其他BSD系統的kqueue,事件端口在SunOS 和IOCP在Windows
我也假設http.request是使用libuv這個achive。
相似,如果我需要做一些文件I/O而不阻塞V8線程
我將使用Node的FileSystem模塊。這次libuv主線程不能以非阻塞的方式處理這個問題,因爲操作系統不提供這個功能。
與網絡I/O,沒有特定於平臺的文件I/O原語 libuv可以依靠,所以目前的辦法是運行阻止文件 I/O操作的線程池。
在這種情況下,爲了不阻止libuv事件循環,需要經典的線程池。
現在,如果我需要查詢數據庫,所有不阻止V8線程和libuv線程的責任都在驅動程序開發人員的手中。 如果驅動程序不使用libuv,它將阻止V8引擎。
相反,如果它使用libuv但基礎數據庫不具有異步功能,那麼它將block a worker thread。
最後,如果數據庫提供異步功能,它只會阻塞數據庫線程。 (在這種情況下,我可以根本避免libuv並直接從V8線程調用驅動程序)
如果這個結論正確描述,儘管以簡單的方式說明了libuv和V8在Node.js中一起工作的方式,我無法看到使用V8的好處,因爲我們可以直接在libuv中完成所有工作(除非目標是爲開發人員提供一種允許以更簡單的方式編寫基於事件的代碼的語言)。