天天看點

聊聊 tornado 的異步回調

異步回調程式的原理和寫法我不就不介紹了,因為我主要是來吐槽下這種代碼風格的。

最近因為追求性能是以去看了下tornado,然後發現這貨如果你要利用它的高性能 ,那麼你就要寫回調代碼,跟twisted一樣,各種callback。

我們正常的同步代碼一般是這樣的

res = db.query(...)
res2 = dosomething(res)
res3 = db.insert(...)
return res3
           

上面的代碼共四行,2次資料庫操作,一次資料處理最後傳回想要的結果。邏輯很清晰也很易懂,我想大部分的程式都是這樣的。但如如果是異步的寫法就會這麼寫

db.query(...,callback=fun2)

def fun2(res):
    res2 = dosomething(res,callback=fun3)

def fun3(res):
    db.insert(...,callback=fun4)
           

異步的代碼就是隻要你涉及到阻塞的代碼就需要使用回調,呵呵,看到了吧2次阻塞就要求你把一段代碼寫成3段,代碼易讀性直線下降,如果你隻是做一些簡單的功能,代碼也不多,我覺得還是能接受的,畢竟性能真的好,但是如果你要做一個很大的項目,一個請求可能要做4,5次阻塞操作,不敢想象,簡直就是個災難。

不過好在tornado還給了我們另一條活路,那就是tornado.gen http://www.tornadoweb.org/documentation/gen.html。

簡單點說就是gen 給了我們用同步代碼來做異步實作的可能。

我們來看看gen和普通異步代碼。

class BaseHandler(tornado.web.RequestHandler):
    @property
    def db(self):
        if not hasattr(self, "_db"):
            self._db = asyncmongo.Client(pool_id='test_pool', **settings.get('mongo_database'))
        return self._db
    
    def api_response(self, data):
        """将資料轉成json傳回給用戶端"""
        self.set_header("Content-Type", "application/json; charset=UTF-8")
        data = dumps(data)
        self.finish(data)

class A(BaseHandler):
    """
    同步代碼實作異步
    """
    @tornado.web.asynchronous
    @gen.engine
    def get(self):
        res = self.get_user()
          
    @tornado.web.asynchronous
    @gen.engine
    def get_user(self):
        user_id = 'asdf'+str(random.randrange(1,490000)) #随機産生一個資料庫裡有的使用者
        res = yield gen.Task(self.db.user.find,{'name':user_id},limit=1)
        res=res[0][0][0]
        self.api_response(res)
        
class B(BaseHandler):
    """
    異步代碼
    """
    @tornado.web.asynchronous
    def get(self):
        user_id = 'asdf'+str(random.randrange(1,490000))#随機産生一個資料庫裡有的使用者
        self.db.user.find({ 'name': user_id }, callback=self.async_callback(self.finish_save))
    
    def finish_save(self, response, error):
        self.api_response(response[0])
           

最後的最後我分别測試了2中寫法最後在性能上的結果,我用ab -n 10000 -c 200  做了測試

gen 的代碼 用了7秒

普通的異步代碼  用了6.3秒

我覺得gen 略慢吧,但還是在可接受範圍内,畢竟我不用寫你妹的回調了。。。。怨念

當然 如果大家不是寫web 就直接用gevent 吧,連這些gen的裝飾器都可以省了。