2012-09-27 21 views
0

可能重複:
Asynchronous HTTP calls in Python如何從Django視圖進行併發Web服務調用?

我有需要檢索來自多個web服務的搜索結果,結果混合在一起,並且使它們Django視圖。我從來沒有在Django中做過任何多線程。什麼是現代,高效,安全的做法?

我不知道它做任何事,但gevent似乎是一個合理的選擇。我應該使用它嗎?它與Django玩的很好嗎?我應該看看別處嗎?

+0

這不是一個答案(這是一個註釋!),但它可能是值得嘗試的工作移動到客戶端。這樣你就不會束縛服務器資源只是等待響應,(其餘部分)你的頁面加載速度更快,並且在可怕的情況下,其中一個服務被破壞,你的頁面仍然有效。 – dokkaebi

+0

如果你不確定這個過程會花費幾秒鐘的時間,我建議使用任務隊列完成視圖外部的工作。然後將用戶引導至一個簡單的頁面,該頁面通過javascript定期檢查,直到任務顯示完成。一個任務隊列系統的例子是Celery/RabbitMQ –

+0

好點。在我的特殊情況下,Web服務是內部服務(對我的網絡而言),用戶不能直接訪問它,而且應該具有非常低的延遲。 –

回答

2

不確定關於gevent。最簡單的方法是使用線程[*]。這裏有一個如何在Python中使用線程一個簡單的例子:

# std lib modules. "Batteries included" FTW. 
import threading 
import time 

thread_result = -1 

def ThreadWork(): 
    global thread_result 
    thread_result = 1 + 1 
    time.sleep(5) # phew, I'm tiered after all that addition! 

my_thread = threading.Thread(target=ThreadWork) 
my_thread.start() # This will call ThreadWork in the background. 
        # In the mean time, you can do other stuff 
y = 2 * 5 # Completely independent calculation. 
my_thread.join() # Wait for the thread to finish doing it's thing. 
        # This should take about 5 seconds, 
        # due to time.sleep being called 
print "thread_result * y =", thread_result * y 

可以啓動多個線程,每個都有不同的製作Web服務調用,並加入在所有這些線程。一旦所有這些加入調用都返回,結果就會顯示,並且您可以將它們混合。

更高級的技巧:你應該叫一個暫停加盟;否則,您的用戶可能無限期地等待您的應用向他們發送回覆。更好的做法是在請求到達您的應用程序之前進行這些Web服務調用;否則,您的應用程序的響應速度受您依賴的服務的支配。

警告對一般線程:小心,可以由兩個(或多個)不同的線程訪問的數據。訪問相同的數據需要「同步」。最流行的同步設備是一個鎖,但有很多其他的。 threading.Lock實現一個鎖。如果你不太注意同步,你可能會在應用中寫入「競爭條件」。這些錯誤難以調試,因爲它們不能可靠地複製。

以我簡單的例子,thread_result被my_thread和主線程之間共享。我不需要任何鎖,因爲主線程直到my_thread結束才訪問thread_result。如果我沒有調用my_thread.join,結果有時會是-10而不是20.請繼續嘗試。

[*] Python沒有在這個意義上真正的線程併發線程不simulatneously執行,即使你有空閒的內核。但是,您仍然可以同時執行;當一個線程被阻塞時,其他線程可以執行。

+1

謝謝!我最終使用Queue.Queue來同步線程的結果。每個線程檢索結果並將結果放在共享隊列的末尾。主線程爲每個線程在隊列上設置一個阻塞的「get」來收集結果。 –

1

gevent不會幫助您更快地處理任務。在涉及資源佔用情況時,它比線程更高效。當使用Django運行gevent時(通常通過gunicorn),您的web應用程序將能夠處理比正常的django wsgi應用程序更多的併發連接。

:我覺得這無關你的問題。你想要做的是在一個Django視圖中處理一個巨大的任務,這通常不是一個好主意。我個人建議你不要在Django中使用線程或gevents greenlets。我看到獨立的Python腳本或守護進程或其他工具的重點,但不適用於Web。這主要導致不穩定和更多資源佔用。相反,我同意dokkaebiAndrew Gorcester的意見。儘管這兩種評論都有所不同,因爲它真的取決於你的任務。

  1. 如果您可以將任務分成許多較小的任務,您可以創建多個視圖來處理這些子任務。這些視圖可能會返回類似JSON的內容,並且可以通過前端的AJAX使用。像這樣,您可以在頁面「進入」時構建頁面的內容,並且用戶無需等待整個頁面加載。

  2. 如果你的任務是一個巨大的塊,你最好用任務隊列處理器。想到了Celery。如果芹菜太過分了,你可以使用zeroMQ。這基本上就像Andrew上面提到的那樣工作:你安排任務進行處理,並從前端頁面輪詢後端直到任務完成(通常也通過AJAX)。你也可以在這裏使用類似long polling的東西。

+0

你說得對,gevent是一隻紅鯡魚。謝謝!我真的很想在一個視圖中將多個服務調用的結果組合起來。我承認,通常你不應該這樣做,但對於我的用例,我確信這是正確的路線。 –

1

我只是很好地解決了使用futuresavailable in 3.2backported to earlier versions including 2.x這個問題。

在我來說,我是檢索從內部服務結果和整理他們:

def _getInfo(request,key): 
    return urllib2.urlopen(
     'http://{0[SERVER_NAME]}:{0[SERVER_PORT]}'.format(request.META) + 
     reverse('my.internal.view', args=(key,)) 
     , timeout=30) 

… 

    with futures.ThreadPoolExecutor(max_workers=os.sysconf('SC_NPROCESSORS_ONLN')) as executor: 
     futureCalls = dict([ (
      key,executor.submit(getInfo,request,key) 
     ) for key in myListOfItems ]) 
     curInfo = futureCalls[key] 
     if curInfo.exception() is not None: 
      # "exception calling for info: {0}".format(curInfo.exception())" 
     else: 
      # Handle the result…