2014-09-30 70 views
6

我有一個燒瓶應用程序,它將給定的URL連接到外部服務(具有不同但通常較長的響應時間)並在那裏搜索某些內容。之後,對檢索到的數據有一些CPU繁重的操作。這也需要一些時間。燒瓶和/或龍捲風 - 處理耗時的外部網絡服務調用

我的問題:來自外部的響應可能需要一些時間。你不能做太多的事情,但是當你一次有多個請求時它就會成爲一個大問題 - 對外部服務的請求會阻塞線程,其餘的都在等待。

明顯浪費時間,它會殺死應用程序。

我聽說過這個叫做Tornado的異步庫。並有我的問題:

  1. 這是否意味着它可以設法處理多個請求,並在外部響應後立即觸發回調?
  2. 我可以用我當前的燒瓶應用程序(可能不是因爲我猜WSGI?)實現嗎?或者我需要將整個應用程序重寫爲Tornado?
  3. 那些CPU繁重的操作 - 會阻擋我的線程嗎?無論如何,做一些負載平衡是個好主意,但我很好奇Tornado是如何處理這個問題的。
  4. 可能的陷阱,陷阱?

回答

5

內置於燒瓶中的Web服務器並不打算在生產環境中使用,這正是您列出的原因 - 它是單線程的,並且如果有任何請求阻塞時間不長,容易陷入困境。燒瓶文件lists several options for deploying it in a production environment; mod_wsgi,gunicorn,uSWGI等。所有這些部署選項都提供了通過線程,進程或非阻塞I/O來處理併發的機制。但是,請注意,如果您正在執行CPU綁定操作,唯一會提供真正併發性的選項是使用多個進程。

如果要使用tornado,則需要用tornado樣式重寫應用程序。由於其架構基於顯式異步I/O,因此如果將其部署爲WSGI應用程序,則無法使用其異步功能。 「tornado風格」基本上意味着對所有I/O操作使用非阻塞API,並使用子進程處理任何長時間運行的CPU綁定操作。該tornado文檔介紹瞭如何進行異步I/O調用,但這裏是它如何工作的一個基本的例子:

from tornado import gen 

@gen.coroutine 
def fetch_coroutine(url): 
    http_client = AsyncHTTPClient() 
    response = yield http_client.fetch(url) 
    return response.body 

response = yield http_client.fetch(curl)調用實際上是異步的;它將在請求開始時將控制權返回給龍捲風事件循環,並且一旦收到響應就會再次恢復。這允許多個異步HTTP請求同時運行,全部在一個線程內運行。但請注意,fetch_coroutine中的任何內容不是異步I/O將阻止事件循環,並且在代碼運行時不能處理其他請求。

要處理長時間運行的CPU綁定操作,您需要將工作發送到子進程以避免阻塞事件循環。對於Python,通常意味着使用multiprocessingconcurrent.futures。我想看看this question以獲取更多關於如何最好地將這些庫與tornado進行整合的信息。請注意,您不希望維護大於系統中CPU數量的進程池,因此請考慮在指定時間的情況下,您希望在任何給定時間運行多少個併發CPU限制操作將其擴展到單臺機器之外。

龍捲風文檔has a section dedicated to running behind a load balancer也是如此。他們建議爲此使用NGINX。

1

龍捲風似乎比Flask更適合這項工作。在tornado.ioloop的實例中運行的Tornado.web.RequestHandler的子類應該爲您提供非阻塞請求處理。我期望它看起來像這樣。

import tornado 
import tornado.web 
import tornado.ioloop 
import json 

class handler(tornado.web.RequestHandler): 
    def post(self): 
     self.write(json.dumps({'aaa':'bbbbb'})) 


if __name__ == '__main__': 
    app = tornado.web.Application([('/', handler)]) 
    app.listen(80, address='0.0.0.0') 
    loop = tornado.ioloop.IOLoop.instance() 
    loop.start() 

,如果你想你的帖子的處理程序是異步的,你可以用tornado.gen.coroutine與「AsyncHTTPClient or grequests`裝飾。這會給你無阻塞的請求。儘管我並不完全確定,但你也可能把你的計算放在協同程序中。

+0

只要將'@ tornado.gen.coroutine'裝飾器添加到處理程序中,不會使其異步。你必須在處理程序中實際進行非阻塞調用。如果你在函數中做了任何阻塞操作,無論你是否使用'coroutine'裝飾器,整個事件循環都會被阻塞。 – dano 2014-10-01 01:05:42

+0

你是對的,我的錯。你可以使用grequests來獲得異步請求嗎? – ragingSloth 2014-10-01 01:50:04

+0

'grequests'使用'gevent'來提供異步I/O,是的。如果你使用'tornado',你可能會想使用它的內置'AsyncHTTPClient',因爲它與龍捲風事件循環集成在一起。 – dano 2014-10-01 02:28:58