Reactor pattern in Tornado

前言: 在看 tornado v1.0 的服務器部分的源碼時,當時傻乎乎的還不懂啥是 reactor 設計模式,看得真心是頭痛!那時,只知道一個叫 單例模式的。看來,軟件的設計架構還是真心有用的。(這是個套路...)

接下來就簡單的分析一下 tornado 中的 reactor pattern, 由于才疏學淺,難免有錯,還請指教!
預備知識:知道 socket, epoll 的原理及使用,當然,最好也是看了 tornado 中的服務器部分的源碼以及這篇 Reactor: An Object Behavioral Pattern for
Demultiplexing and Dispatching Handles for Synchronous Events

What's Reactor

來自 wikipedia 的簡明版:

The reactor design pattern is an event handling pattern for handling service requests delivered concurrently to a service handler by one or more inputs. The service handler then demultiplexes the incoming requests and dispatches them synchronously to the associated request handlers.

一個 Reactor 中通常有幾個元素:

  • Resources
    Any resource that can provide input to or consume output from the system.

  • Synchronous Event Demultiplexer
    Uses an event loop to block on all resources. The demultiplexer sends the resource to the dispatcher when it is possible to start a synchronous operation on a resource without blocking (Example: a synchronous call to read() will block if there is no data to read. The demultiplexer uses select() on the resource, which blocks until the resource is available for reading. In this case, a synchronous call to read() won't block, and the demultiplexer can send the resource to the dispatcher.)

  • Dispatcher
    Handles registering and unregistering of request handlers. Dispatches resources from the demultiplexer to the associated request handler.

  • Request Handler
    An application defined request handler and its associated resource.

來自這篇 Reactor 文章的詳細版

The Reactor design pattern handles service requests that are
delivered concurrently to an application by one or more
clients. Each service in an application may consist of
serveral methods and is represented by a separate event handler
that is responsible for dispatching service-specific requests.
Dispatching of event handlers is performed by an initiation
dispatcher, which manages the registered event handlers.
Demultiplexing of service requests is performed by a
synchronous event demultiplexer.

  • Handles
    Identify resources that are managed by an OS. These resources commonly include network connections, open files, timers, synchronization objects, etc.

  • Synchronous Event Demultiplexer
    Blocks awaiting events to occur on a set of Handles. It returns when it is possible to initiate an operation on a Handle without blocking. A common demultiplexer for I/O events is select, which is an event demultiplexing system call provided by the UNIX and Win32 OS platforms. The select call indicates which Handles can have operations invoked on them synchronously without blocking the application process.

  • Initiation Dispatcher
    Defines an interface for registering, removing, and dispatching Event Handlers. Ultimately, the Synchronous Event Demultiplexer is responsible for waiting until new events occur. When it detects new events, it informs the Initiation Dispatcher to call back application-specific event handlers. Common events include connection acceptance events, data input and output events, and timeout events.

  • Event Handler
    Specifies an interface consisting of a hook method that abstractly represents the dispatching operation for service-specific events. This method must be implemented by application-specific services.

  • Concrete Event Handler
    Implements the hook method, as well as the methods to process these events in an application-specific manner. Applications register Concrete Event Handlers with the Initiation Dispatcher to process certain types of events. When these events arrive, the Initiation Dispatcher calls back the hook method of the appropriate Concrete Event Handler.

reactor_structure

Reactor structure in Tornado

由上面對 reactor structure 的理解,進一步推理到 Tornado 中:

  • handle 或是 resource 就是指一個網絡連接 socket;

  • 一個 event 就是這個 socket 監聽的類型, 比如 read or write etc;

  • handler, 在tornado 中就是一個 RequestHandler class. 上面的 Event Handler 對應 tornado 中 RequestHandler 基類,而 Concrete Event Handler 對應 tornado 中用戶自定義的繼承 RequestHandler 的類, 如下:

class MainHandler(tornado.web.RequestHandler):
    def get(self):
        self.write("Hello, world")
  • Synchronous Event Demultiplexer (或者稱它為 Notifier) 就如同 epoll 或者 select, 用于監聽注冊了特定 event 事件的 handle.

  • Initiation Dispatcher 就是 tornado 中的 IOLoop.

General Collaborations

各模塊之間的協作,如下圖所示.

reactor_work

請注意上圖最左邊縱向的字為: INITIALIZATION MODE, EVENT HANDLING MODE. 這意味著可以將這個模式的工作方式分解成這2個小模塊來分析.

Initialization Mode

  • 1 When an application registers a Concrete Event Handler with the Initiation Dispatcher the application indicates the type of event(s) this Event Handler wants the Initiation Dispatcher to notify it about when the event(s) occur on the associated Handle.

  • 2 The Initiation Dispatcher requests each Event Handler to pass back its internal Handle. This Handle identifies the Event Handler to the
    OS.

  • 3 After all Event Handlers are registered, an application calls handle events to start the Initiation Dispatcher’s event loop. At this point, the Initiation Dispatcher combines the Handle from each registered Event Handler and uses the Synchronous Event Demultiplexer to wait for events to occur on these Handles. For instance, the TCP protocol layer uses the select synchronous event demultiplexing operation to wait for client logging record events to arrive on connected socket Handles.

Event Handling Mode

  • 1 The Synchronous Event Demultiplexer notifies the Initiation Dispatcher when a Handle corresponding to an event source becomes “ready,” e.g., that a TCP socket is “ready for reading.”

  • 2 The Initiation Dispatcher triggers Event Handler hook method in response to events on the ready Handles. When events occur, the Initiation Dispatcher uses the Handles activated by the event sources as “keys” to locate and dispatch the appropriate Event Handler’s hook method.

  • 3 The Initiation Dispatcher calls back to the handle event hook method of the Event Handler to perform application-specific functionality in response to an event. The type of event that occurred can be passed as a parameter to the method and used internally by this method to perform additional service specific demultiplexing and dispatching.

Initialization Mode in Tornado

  • 1 在 Application 類中,生成 url 映射, 一個url對應一個XXXRequestHandler處理方法; 在服務端中,創建了socket對象, 單例模式創建IOLoop對象,然后將socket對象句柄作為key,被封裝了的函數_handle_events作為value,添加到IOLoop對象的_handlers字段中(向Initiation Dispatcher 中注冊 Handlers, 建議 socket fd 到 handler 的映射)

  • 2 向epoll中注冊監聽服務端socket對象的讀可用事件(向 Synchronous Event Demultiplexer 中注冊 handle 監聽的 event 類型)

如下為 tornado v1.0.0 代碼

class IOLoop(object):
    ...
    def add_handler(self, fd, handler, events):
        """Registers the given handler to receive the given events for fd."""
        self._handlers[fd] = handler
        self._impl.register(fd, events | self.ERROR)
  • 3 開始IOLoop 調用 start() 函數開始Event Loop, epoll 開始監聽注冊了的事件(對應Initiation Dispatcher 調用 handle_events() );
class IOLoop(object):
    ...
    def start(self):
        """Starts the I/O loop.
        The loop will run until one of the I/O handlers calls stop(), which
        will make the loop stop after the current event iteration completes.
        """
        ...
        while True:
            # Never use an infinite timeout here - it can stall epoll
            poll_timeout = 0.2
            ...
            event_pairs = self._impl.poll(poll_timeout)
            ...
            self._events.update(event_pairs)
            while self._events:
                fd, events = self._events.popitem()
                ...
                    self._handlers[fd](fd, events)
                ...

Event Handling Mode in Tornado

  • 1 當 epoll 監聽到一個或多個事件到來時,將其放到 event_pairs 中(如上面代碼所示, Synchronous Event Demultiplexer通知Initiation Dispatcher handle 的到來 );

  • 2,3 根據handle 即 socket fd, IOLoop 找到并調用注冊了的 XXXRequestHandler 中的hood method, 比如 get, post..給予響應,返回 response 給 client;

Others

Define the Event Handling Interface

對應 tornado 中 RequestHandler 類的接口實現,有兩種方法: 一種是 A single-method interface, 另一種是 A multi-method interface; 而 Tornado 中 RequestHandler 采用的是后種即 A multi-method Interface 的設計方法,因為HTTP/1.1 總共就8種方法: GET HEAD POST OPTIONS PUT DELETE TRACE CONNECT;

class RequestHandler(object):
   ...
   def head(self, *args, **kwargs):
       raise HTTPError(405)

   def get(self, *args, **kwargs):
       raise HTTPError(405)
   ...

Determine the Number of Initiation Dispatchers in an Application

"Many applications can be structured using just one instance of the Reactor pattern. In this case, the Initiation Dispatcher can be implemented as a Singleton".

而 Tornado 中的 IOLoop 就是采用 Singleton 模式的實現;

Reference

See also

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 229,517評論 6 539
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,087評論 3 423
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 177,521評論 0 382
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,493評論 1 316
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,207評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,603評論 1 325
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,624評論 3 444
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,813評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,364評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,110評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,305評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,874評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,532評論 3 348
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,953評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,209評論 1 291
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,033評論 3 396
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,268評論 2 375

推薦閱讀更多精彩內容

  • 什么是Reactor模式 要回答這個問題,首先當然是求助Google或Wikipedia,其中Wikipedia上...
    wiseAaron閱讀 5,008評論 4 7
  • reactor Understanding Reactor Pattern with Java NIOreacto...
    礪豪閱讀 2,316評論 0 1
  • 收集,我們在生活中我們有很多事都是說了沒有做,可又在做別的事時又想起來了,這樣就把我們的注意力給分散了,使我們沒法...
    c32316c20a41閱讀 91評論 0 0
  • 非監督學習問題: 主要使用 RBM,Autoencoders。 監督學習問題: 例如文字處理,圖像識別,物體識別,...
    重新出發_砥礪前行閱讀 282評論 0 0
  • 受夠了這樣的生存。 小鬼生病有些時刻了,喉嚨發出嘶啞的聲音,眼睛上粘著的液體表示上火,我束無對策著急,以為它被掐著...
    宋小朝閱讀 98評論 0 0