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

溫馨提示×

溫馨提示×

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

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

Tornado實現多進程/多線程的HTTP服務詳解

發布時間:2020-10-26 09:17:09 來源:腳本之家 閱讀:295 作者:ZhiChao& 欄目:開發技術

用tornado web服務的基本流程

1.實現處理請求的Handler,該類繼承自tornado.web.RequestHandler,實現用于處理請求的對應方法如:get、post等。返回內容用self.write方法輸出。

2.實例化一個Application。構造函數的參數是一個Handlers列表,通過正則表達式,將請求與Handler對應起來。通過dict將Handler需要的其他對象以參數的方式傳遞給Handler的initialize方法。

3.初始化一個tornado.httpserver.HTTPServer對象,構造函數的參數是上一步的Application對象。

4.為HTTPServer對象綁定一個端口。

5.開始IOLoop。

需要用到的特性

由于tornado的亮點是異步請求,所以這里首先想到的是將所有請求都改造為異步的。但是這里遇到一個問題,就是異步函數內一定不能有阻塞調用出現,否則整個IOLoop都會被卡住。這就要求徹底地去改造服務,將所有IO或是用時較長的請求都改造為異步函數。這個工程量是非常大的,需要去修改已有的代碼。因此,我們考慮用線程池的方式去實現。當一個線程阻塞在某個請求或IO時,其他線程或IOLoop會繼續執行。

另外一個瓶頸就是GIL限制了CPU的并發數量,因此考慮用子進程的方式增加進程數,提高服務能力上限。

綜合上面的分析,大致用以下方案:

  • 通過子進程的方式復制多個進程,使子進程中的只讀頁指向同一個物理頁。
  • 線程池。回避異步改造的工作量,增加IO的并發量。

測試代碼

首先測試線程池,測試用例為:

對sleep頁面同時發出兩個請求:

  • 在線程池中運行的函數(這里是self.block_task)能夠同時執行。表現為在控制臺交替打印出數字。
  • 兩個get請求幾乎同時返回,在瀏覽器上顯示返回的內容。

線程池的測試代碼如下:

import os
import sys
import time
 
import tornado.httpserver
import tornado.ioloop
import tornado.options
import tornado.web
import tornado.gen
from tornado.concurrent import run_on_executor
from concurrent.futures import ThreadPoolExecutor
from tornado.options import define, options
 
class HasBlockTaskHandler(tornado.web.RequestHandler):
  executor = ThreadPoolExecutor(20)  #起線程池,由當前RequestHandler持有
   
  @tornado.gen.coroutine
  def get(self):
    strTime = time.strftime("%Y-%m-%d %H:%M:%S")
    print "in get before block_task %s" % strTime
    result = yield self.block_task(strTime)
    print "in get after block_task"
    self.write("%s" % (result))
 
  @run_on_executor
  def block_task(self, strTime):
    print "in block_task %s" % strTime
    for i in range(1, 16):
      time.sleep(1)
      print "step %d : %s" % (i, strTime)
    return "Finish %s" % strTime
 
if __name__ == "__main__":
  tornado.options.parse_command_line()
  app = tornado.web.Application(handlers=[(r"/sleep", HasBlockTaskHandler)], autoreload=False, debug=False)
  http_server = tornado.httpserver.HTTPServer(app)
  http_server.bind(8888)
  tornado.ioloop.IOLoop.instance().start()

整個代碼里有幾個位置值得關注:

1.executor = ThreadPoolExecutor(20)。這是給Handler類初始化了一個線程池。其中concurrent.futures不屬于tornado,是python的一個獨立模塊,在python3中是內置模塊,python2.7需要自己安裝。

2.修飾符@run_on_executor。這個修飾符將同步函數改造為在executor(這里是線程池)上運行的異步函數,內部實現是將被修飾的函數submit到executor,返回一個Future對象。

3.修飾符@tornado.gen.coroutine。被這個修飾符修飾的函數,是一個以同步函數方式編寫的異步函數。原本通過callback方式編寫的異步代碼,有了這個修飾符,可以通過yield一個Future的方式來寫。被修飾的函數在yield了一個Future對象后將會被掛起,Future對象的結果返回后繼續執行。

運行代碼后,在兩個不同瀏覽器上訪問sleep頁面,得到了想要的效果。這里有一個小插曲,就是如果在同一瀏覽器的兩個tab上進行測試,是無法看到想要的效果。第二個get請求會被block,直到第一個get請求返回,服務端才開始處理第二個get請求。這讓我一度覺得多線程沒有生效,用了半天時間查了很多資料,才看到是瀏覽器把相同的第二個請求block了,具體鏈接參考這里。

由于tornado很方便地支持多進程模型,多進程的使用要簡單很多,在以上例子中,只需要對啟動部分稍作改動即可。具體代碼如下所示:

if __name__ == "__main__":
  tornado.options.parse_command_line()
  app = tornado.web.Application(handlers=[(r"/sleep", HasBlockTaskHandler)], autoreload=False, debug=False)
  http_server = tornado.httpserver.HTTPServer(app)
  http_server.bind(8888)
  print tornado.ioloop.IOLoop.initialized()
  http_server.start(5)
  tornado.ioloop.IOLoop.instance().start()

需要注意的地方有兩點:

1.app = tornado.web.Application(handlers=[(r"/sleep", HasBlockTaskHandler)], autoreload=False, debug=False),在生成Application對象時,要將autoreload和debug兩個參數至為False。也就是需要保證在fork子進程之前IOLoop是未被初始化的。這個可以通過tornado.ioloop.IOLoop.initialized()函數來跟。

2.http_server.start(5)在啟動IOLoop之前通過start函數設置進程數量,如果設置為0表示每個CPU都啟動一個進程。
最后的效果是可以看到n+1個進程在運行,且公用同一個端口。

以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持億速云。

向AI問一下細節

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

AI

平山县| 同德县| 千阳县| 明光市| 灵山县| 宁海县| 凤城市| 夹江县| 柳河县| 无极县| 华容县| 霍州市| 湘乡市| 藁城市| 宁河县| 乐安县| 玉林市| 福贡县| 新营市| 新乐市| 乌拉特前旗| 巴彦淖尔市| 定远县| 阿巴嘎旗| 天台县| 芜湖县| 香河县| 肃南| 和龙市| 分宜县| 常熟市| 陵川县| 库尔勒市| 墨竹工卡县| 合山市| 武义县| 绥化市| 康乐县| 柳州市| 中西区| 林州市|