中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

Tornado 多進程實現分析詳解

發布時間:2020-10-16 15:52:17 來源:腳本之家 閱讀:642 作者:mingz2013 欄目:開發技術

引子

Tornado 是一個網絡異步的的web開發框架, 并且可以利用多進程進行提高效率, 下面是創建一個多進程 tornado 程序的例子.

#!/usr/bin/env python
# -*- coding:utf-8 -*-
import os
import time

import tornado.web
import tornado.httpserver
import tornado.ioloop
import tornado.netutil
import tornado.process


class LongHandler(tornado.web.RequestHandler):

	def get(self):
		self.write(str(os.getpid()))
		time.sleep(10)


if __name__ == "__main__":
	app = tornado.web.Application(([r'/', LongHandler], ))
	sockets = tornado.netutil.bind_sockets(8090)
	tornado.process.fork_processes(2)
	server = tornado.httpserver.HTTPServer(app)
	server.add_sockets(sockets)
	tornado.ioloop.IOLoop.instance().start()

上面代碼使用 tornado.process.fork_processes 創建了2個子進程, 同時用時訪問這個 服務兩次, 分別會返回兩個相鄰的pid. 可以看到 tornado 確實使用了兩個進程來同時完成任務.

我一直很好奇 tornado 是如何將請求調度到子進程, 多個子進程又如何不同時處理一個請求呢?

探究

我們首先是調用 tornado.netutil.bind_sockets 來創建一個 socket(或一個 socket 列表),

接著我們調用 tornado.process.fork_processes 來 fork 子進程, 閱讀此函數的代碼會發現這個函數僅僅是創建子進程, 然后主進程負責等待子進程, 如果子進 程退出則會根據條件重啟子進程, 如果子進程全部退出并不符合重啟條件,則主進程退出.

調用這個函數之后, 子進程中函數會返回, 子進程則繼續執行調用這個函數之后的代碼.

我們在 fork 子進程后做了如下操作.

server = tornado.httpserver.HTTPServer(app)
  server.add_sockets(sockets)
  tornado.ioloop.IOLoop.instance().start()

我們先看看 tornado.httpserver.HTTPServer.add_sockets 發現 HTTPServer是繼承的 tornado.netutil.TCPServer , add_sockets 也是實現在 TCPServer 中

tornado.netutil.TCPServer.add_sockets

def add_sockets(self, sockets):
		if self.io_loop is None:
			self.io_loop = IOLoop.instance()

		for sock in sockets:
			self._sockets[sock.fileno()] = sock
			add_accept_handler(sock, self._handle_connection,
							  io_loop=self.io_loop)

主要是映射了下 socket 和 socket 對應的文件描述符, 我們看看它調用的 add_accept_handler

def add_accept_handler(sock, callback, io_loop=None):
	if io_loop is None:
		io_loop = IOLoop.instance()

	def accept_handler(fd, events):
		while True:
			try:
				connection, address = sock.accept()
			except socket.error as e:
				if e.args[0] in (errno.EWOULDBLOCK, errno.EAGAIN):
					return
				raise
			callback(connection, address)
	io_loop.add_handler(sock.fileno(), accept_handler, IOLoop.READ)

我們知道 I/O多路復用 在處理服務端 socket 時, 當有連接請求過來時, 會觸發 可讀的事件, 此函數將 socket 在主事件循環中注冊讀事件(IOLoop.READ), 它的回調 會創建連接, 我注意到回調里的異常捕獲有這樣幾行

if e.args[0] in (errno.EWOULDBLOCK, errno.EAGAIN):
          return
        raise

發現在創建連接的時候會跳過這個異常呢, 為什么?那么 EWOULDBLOCK 和 EAGAIN是是什么呢? 通過查找知道它的意思是在非阻塞模式下, 不需要重讀或重寫, EAGAIN 是 EWOULDBLOCK 在 Windows 上的名字, 所以看到這里就很明確了.

結論

Tornado 多進程的處理流程是先創建 socket, 然后再 fork 子進程, 這樣所有的子進程實際都監聽 一個(或多個)文件描述符, 也就是都在監聽同樣的 socket.

當連接過來所有的子進程都會收到可讀事件, 這時候所有的子進程都會跳到 accept_handler 回調函數, 嘗試建立連接.

一旦其中一個子進程成功的建立了連接, 當其他子進程再嘗試建立這個連接的時候就會觸發 EWOULDBLOCK (或 EAGAIN) 錯誤. 這時候回調函數判斷是這個錯誤則返回函數不做處理.

當成功建立連接的子進程還在處理這個連接的時候又過來一個連接, 這時候就會有另外一個 子進程接手這個連接.

Tornado 就是通過這樣一種機制, 利用多進程提升效率, 由于連接只能由一個子進程成功創建, 同一個請求也就不會被多個子進程處理.

后記

寫完才發現, 我所使用的代碼是 tornado-2.4.post2 版本, 當前最新代碼是 3.3.0, 查看了下最新代碼, 最新代碼 TCPServer 寫到單獨 tornado.tcpserver 里了, 其他和本文 相關的并沒有什么大的變化.

Category:PythonTagged:Pythonfork_processestornado多進程web提升效率

以上就是本文關于Tornado 多進程實現分析詳解的全部內容,希望對大家有所幫助。感興趣的朋友可以繼續參閱本站其他相關專題,如有不足之處,歡迎留言指出。感謝朋友們對本站的支持!

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

龙川县| 诸暨市| 平邑县| 鲁甸县| 武威市| 个旧市| 五河县| 香港| 来凤县| 息烽县| 长春市| 伊宁市| 海丰县| 武胜县| 高平市| 宝清县| 嘉黎县| 共和县| 五大连池市| 建阳市| 许昌县| 兰考县| 宿州市| 宿松县| 沽源县| 富平县| 黄梅县| 温州市| 广西| 遂川县| 秦皇岛市| 亚东县| 龙泉市| 马尔康县| 上饶市| 达尔| 宣城市| 玉溪市| 托克托县| 乌兰县| 稷山县|