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阅读 204,684评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,143评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,214评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,788评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,796评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,665评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,027评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,679评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 41,346评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,664评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,766评论 1 331
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,412评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,015评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,974评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,073评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,501评论 2 343

推荐阅读更多精彩内容

  • 什么是Reactor模式 要回答这个问题,首先当然是求助Google或Wikipedia,其中Wikipedia上...
    wiseAaron阅读 4,972评论 4 7
  • reactor Understanding Reactor Pattern with Java NIOreacto...
    砺豪阅读 2,296评论 0 1
  • 收集,我们在生活中我们有很多事都是说了没有做,可又在做别的事时又想起来了,这样就把我们的注意力给分散了,使我们没法...
    c32316c20a41阅读 85评论 0 0
  • 非监督学习问题: 主要使用 RBM,Autoencoders。 监督学习问题: 例如文字处理,图像识别,物体识别,...
    重新出发_砥砺前行阅读 261评论 0 0
  • 受夠了這樣的生存。 小鬼生病有些時刻了,喉嚨發出嘶啞的聲音,眼睛上粘著的液體表示上火,我束無對策著急,以為它被掐著...
    宋小朝阅读 90评论 0 0