Tornado異步--原理以及普通同步方法改異步


但是對於普通的阻塞操作——比如MySql查詢,甚至是簡單的一句time.sleep--怎么讓其不阻塞呢?

回答這個問題首先要了解Tornado異步的原理。Tornado異步的核心是ioloop.py和iostream.py這兩個文件。ioloop.py實現了一個處理I/O事件的循環,iostream封裝了非阻塞的socket並把I/O事件注冊到ioloop上。Tornado的異步在linux平台基於epoll,它是基於事件而非輪詢的,這是其高效的原因(windows平台沒有epoll,tornado只能使用select,效率比epoll低)。

ps:tornado異步的原理我覺得沒有完全理解,但簡單的講就是這樣了。關於epoll和socket可以看這里

知道原理后,再看這個問題。首先要說:Mysql查詢和time.sleep這兩個操作是不同類型的阻塞操作

python進行Mysql查詢需要建立socket連接,這就可以通過iostream將其改為異步,但是現在通用的mysql庫使用c寫的python模塊,socket部分由c寫完並編譯,就不能修改了。如果是用python寫的mysql庫(如myconnpy),使用的是python的socket,是可以改為異步的庫。現在mongodb和pgsql都有異步的python驅動。

再說time.sleep,這種阻塞操作不需要socket,要將其改為異步,只能使用隊列,線程等方式。這里提供幾種方式:

第一種使用celery,它使用RabbitMQ做后端,使用隊列的方式實現異步,對應tornado有開源的tornado-celery,可以實現異步:

from celery import Celery
import tcelery
from tornado import gen.coroutine

celery = Celery('tasks', backend='redis://localhost', broker='amqp://')

@celery.task
def test(strs):
    time.sleep(5)
    return strs

class MainHandler(tornado.web.RequestHandler):
    @gen.coroutine
    def get(self):
        result = yield tcelery.celery_task(test, "hello world")
        self.write("%s" % result )

第二種使用concurrent.futures,這個並發庫在python3自帶 ,在python2需要安裝sudo pip install futures。這種方法是使用線程池或者進程池的方式實現異步的,它有ThreadPoolExecutorProcessPoolExecutor,要求Handler類要有一個名為executor的屬性,指向一個Executor對象,代碼如下:

from concurrent.futures import ThreadPoolExecutor

class SleepHandler(tornado.web.RequestHandler):
    executor = ThreadPoolExecutor(2)

    @tornado.web.asynchronous
    @tornado.gen.coroutine
    def get(self):
        res = yield self.sleep()
        self.write("sleep %s s" % res)
        self.finish()

    @run_on_executor
    def sleep(self):
        time.sleep(5)
        return 5

上面ThreadPoolExecutor(2),建立了size為2的一個線程池。

這樣SleepHandler就不會阻塞其他Handler。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM