Reactor模式

反應(yīng)器設(shè)計(jì)模式(Reator pattern)是一種基于事件驅(qū)動(dòng)的設(shè)計(jì)模式,常用于高并發(fā)場(chǎng)景下,常見的像Node.js、Netty、Vert.x中都有著Reactor模式的身影。本文對(duì)Reactor模式作了簡要介紹,結(jié)合對(duì)比Node.js線程模型進(jìn)行分析。

單線程Reactor模式

Reactor模式是一種事件處理模式,單個(gè)或多個(gè)事件并發(fā)地投遞到事件處理服務(wù),事件處理服務(wù)將事件進(jìn)行分離,同步的將他們分發(fā)到對(duì)應(yīng)的事件處理器中。

模式結(jié)構(gòu)

下圖是一張示意圖,結(jié)合了維基百科的定義:

  • Handle:句柄,是對(duì)資源在操作系統(tǒng)層面的抽象(unix中的fd),可供系統(tǒng)輸入或輸出的資源。在網(wǎng)絡(luò)編程中,一般指的是一個(gè)連接,如socket,Java NIO中的Channel
  • Demultiplexer:同步事件分離器,通常使用EventLoop來進(jìn)行資源的阻塞等待,當(dāng)一個(gè)資源處于就緒狀態(tài)的時(shí)候,會(huì)被輪詢出來傳遞給分發(fā)器
  • Dispatcher:分發(fā)器,用來注冊(cè)、移除EventHandler,將資源分配到對(duì)應(yīng)的處理器中同步執(zhí)行
  • EventHandler:事件處理器,處理對(duì)應(yīng)的事件,一般由分發(fā)器進(jìn)行回調(diào)。

交互過程

  • 初始化Dispatcher
  • 注冊(cè)EventHandler到Dispatcher,每個(gè)事件處理器包含對(duì)應(yīng)Handle的引用,這樣就可以建立Handle到EventHandler的映射
  • 啟動(dòng)EventLoop,阻塞等待資源的某個(gè)事件發(fā)生
  • 當(dāng)某些Handle的事件發(fā)生后,資源變?yōu)榫途w狀態(tài),會(huì)被傳遞給Dispatcher,分發(fā)器通過開始注冊(cè)的資源映射關(guān)系,調(diào)用對(duì)應(yīng)的EventHandler方法。

這就是最簡單的Reactor模式,它其實(shí)也是I/O多路復(fù)用的一種具現(xiàn),用戶不需要考慮并發(fā)的問題,直接交給了事件處理器進(jìn)行,同時(shí)也減少了高并發(fā)情況下多線程對(duì)系統(tǒng)資源的消耗。

在一些小容量的場(chǎng)景下,單線程的模式可以使用,但在高負(fù)載、大并發(fā)的場(chǎng)景下卻不適用,主要在于一個(gè)NIO線程無法支撐同時(shí)處理成百上千的鏈路處理,在編解碼消息時(shí),由于無法及時(shí)處理會(huì)造成消息堆積,進(jìn)而形成請(qǐng)求超時(shí)等問題,而且單個(gè)線程也不利于程序的穩(wěn)定性,一旦線程崩了,整個(gè)程序就崩掉了。所以在實(shí)際應(yīng)用中大部分都是多線程的Reactor模式。

Node.js線程模型

Node.js是一個(gè)采用事件驅(qū)動(dòng)和異步非阻塞I/O,實(shí)現(xiàn)的單線程、高并發(fā)的JavaScript運(yùn)行環(huán)境,使用它可以編寫性能良好的web服務(wù)端。筆者在學(xué)習(xí)Vert.x的時(shí)候發(fā)現(xiàn)它與Node.js的線程模型很類似,它并不是嚴(yán)格意義上的Reactor模式,這里用來與單線程的Reactor模式作對(duì)比,加深理解。

眾所周知,I/O操作絕大部分都十分耗時(shí),傳統(tǒng)的方法是使用多線程來解決I/O耗時(shí)阻塞的問題,多路復(fù)用也可以實(shí)現(xiàn)同時(shí)處理多個(gè)任務(wù)的功能,那么單線程的IO是如何處理I/O的并發(fā)請(qǐng)求呢?

Node.js并不是單純的單線程,它用主線程處理所有請(qǐng)求,然后對(duì)I/O操作進(jìn)行異步處理,交給其他線程去執(zhí)行,避免了頻繁創(chuàng)建、銷毀和上下文切換帶來的系統(tǒng)開銷。下面來看Node.js的工作原理。

工作原理

從左到右,從上到下,Node.js 被分為了四層,分別是 應(yīng)用層、V8引擎層、Node API層 和 LIBUV層。

  • 應(yīng)用層: 即 JavaScript 交互層,常見的就是 Node.js 的模塊,比如 http,fs
  • V8引擎層: 即利用 V8 引擎來解析JavaScript 語法,進(jìn)而和下層 API 交互
  • NodeAPI層: 為上層模塊提供系統(tǒng)調(diào)用,一般是由 C 語言來實(shí)現(xiàn),和操作系統(tǒng)進(jìn)行交互
  • LIBUV層: 是跨平臺(tái)的底層封裝,實(shí)現(xiàn)了 事件循環(huán)、文件操作等,是 Node.js 實(shí)現(xiàn)異步的核心

Node.js在主線程維護(hù)了一個(gè)事件隊(duì)列,接收到請(qǐng)求后,就將該請(qǐng)求作為一個(gè)事件放入Event Queue中,然后繼續(xù)接受其他請(qǐng)求,當(dāng)主線程空閑(沒有請(qǐng)求接收) 的時(shí)候,就開始輪詢事件隊(duì)列。這里要分兩種情況:

  • 普通任務(wù),就由主線程親自執(zhí)行,并通過回調(diào)函數(shù)返回給上層調(diào)用
  • I/O任務(wù),就從線程池中拿出一個(gè)線程處理這個(gè)事件,指定回調(diào)函數(shù),繼續(xù)輪詢事件隊(duì)列中的其他事件。當(dāng)線程中的I/O任務(wù)完成以后,執(zhí)行回調(diào)函數(shù),并把這個(gè)完成的事件放在事件隊(duì)列的尾部,等待事件循環(huán),當(dāng)主線程再次循環(huán)到該完成事件時(shí),再返回給上層調(diào)用。

Node.js的單線程并非整個(gè)環(huán)境都運(yùn)行在單線程中,而是對(duì)JavaScript層面的任務(wù)處理是單線程的。

像Node.js這種解決方案:將耗時(shí)少的短任務(wù)交給主線程來處理,將I/O操作或者一些CPU密集型任務(wù)交給其他線程來執(zhí)行,這樣不會(huì)阻塞EventLoop正常進(jìn)行事件的循環(huán),是比較通用的解決方案。例如在Vert.x中,對(duì)于普通的verticle,會(huì)運(yùn)行在EventLoop線程中,對(duì)于耗時(shí)長的任務(wù)則放在Worker Pool中的線程上運(yùn)行。

多線程下的Reactor模式與Node.js這種解決方案很類似,一個(gè)線程負(fù)責(zé)鏈路的監(jiān)聽、建立,然后將I/O交給其它子線程來完成。后文會(huì)有介紹。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容