開始Tornado的原始碼分析之旅

李文軍d發表於2015-10-01

Tornado 是由 Facebook 開源的一個伺服器“套裝”,適合於做 python 的 web 或者使用其本身提供的可擴充套件的功能,完成了不完整的 wsgi 協議,可用於做快速的 web 開發,封裝了 epoll 效能較好。文章主要以分析 tornado 的網路部分即非同步事件處理與上層的 IOstream 類提供的非同步IO,其他的模組如 web 的 tornado.web 以後慢慢留作分析。

下面開始我們的 Tornado 之旅,看原始碼之前必定需要有一份原始碼了,大家可以去官網下載一份。這裡分析的是 3.1.1。

Tornado 的原始碼組織如下:

tornado網路部分最核心的兩個模組就是ioloop.py與iostream.py,我們主要分析的就是這兩個部分。

ioloop.py 主要的是將底層的epoll或者說是其他的IO多路複用封裝作非同步事件來處理。
iostream.py主要是對於下層的非同步事件的進一步封裝,為其封裝了更上一層的buffer(IO)事件。
我們先來看看 ioloop(文件地址:http://www.tornadoweb.org/en/stable/ioloop.html):

We use epoll (Linux) or kqueue (BSD and Mac OS X) if they are available, or else we fall back on select(). If you are implementing a system that needs to handle thousands of simultaneous connections, you should use a system that supports either epoll or kqueue.

Example usage for a simple TCP server:

import errno
import functools
import ioloop
import socket

def connection_ready(sock, fd, events):
    while True:
        try:
            connection, address = sock.accept()
        except socket.error, e:
            if e.args[0] not in (errno.EWOULDBLOCK, errno.EAGAIN):
                raise
            return
        connection.setblocking(0)
        handle_connection(connection, address)

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.setblocking(0)
sock.bind(("", port))
sock.listen(128)

# 建立一個ioloop 例項
io_loop = ioloop.IOLoop.instance()
# connection_ready 的第一個引數為 sock,即 socket 的返回值
callback = functools.partial(connection_ready, sock)
# 註冊函式,第一個引數是將 sock 轉換為標準的描述符,第二個為回撥函式,第三個是事件型別
io_loop.add_handler(sock.fileno(), callback, io_loop.READ)
io_loop.start()

可以看到在註釋前都是使用了傳統的建立伺服器的方式,不用多介紹,注意就是把套介面設定為非阻塞方式。

建立ioloop例項,這裡是使用了ioloop.IOLoop中的 instance()靜態方法,以 @classmethod 方式包裝。

在後面的add_handler中,程式為我們的監聽套介面註冊了一個回撥函式和一個事件型別。工作方式是這樣,在註冊了相應的事件型別和回撥函式以後,程式開始啟動,如果在相應的套介面上有事件發生(註冊的事件型別)那麼呼叫相應的回撥函式。

當監聽套介面有可讀事件發生,意味著來了一個新連線,在回撥函式中就可以對這個套介面accept,並呼叫相應的處理函式,其實應該是處理函式也設定為非同步的,將相應的連線套介面也加入到事件迴圈並註冊相應的回撥函式,只是這裡沒有展示出來。

在使用非阻塞方式的accept時候常常返回EAGAIN,EWOULDBLOCK 錯誤,這裡採取的方式是放棄這個連線。

轉自:http://www.cnblogs.com/Bozh/

相關文章