2015-01-11 131 views
5

首先,抱歉我的英語不好。 在我的項目中,我有很多I/O網絡請求。主要數據存儲在另一個項目中,訪問由Web API(JSON/XML)提供,輪詢。我們爲每個新用戶會話使用此API(獲取有關用戶的信息)。有時,我們在等待迴應時遇到問題。 我們使用nginx + uwsgi + django。如你所知,Django是同步的(或阻塞)。 我們使用uwsgi和多線程來解決網絡IO等待的問題。 我決定讀一下gevent。我理解合作與搶先式多任務之間的區別。我希望gevent是更好的解決方案,然後uwsgi線程解決此問題(網絡I/O瓶頸)。但結果幾乎相同。有時候,gevent較弱。 也許某處我錯了。請告訴我。Uwsgi與gevent vs線程

這裏是uwsgi配置示例。 GEVENT:

$ uwsgi --http :8001 --module ugtest.wsgi --gevent 40 --gevent-monkey-patch 

線程:

$ uwsgi --http :8001 --module ugtest.wsgi --enable-threads --threads 40 

控制器例如:

def simple_test_action(request): 
    # get data from API without parsing (only for simple I/O test) 
    data = _get_data_by_url(API_URL) 
    return JsonResponse(data, safe=False) 

import httplib 
from urlparse import urlparse 
def _get_data_by_url(url): 
    u = urlparse(url) 
    if str(u.scheme).strip().lower() == 'https': 
     conn = httplib.HTTPSConnection(u.netloc) 
    else: 
     conn = httplib.HTTPConnection(u.netloc) 
    path_with_params = '%s?%s' % (u.path, u.query,) 
    conn.request("GET", path_with_params) 
    resp = conn.getresponse() 
    print resp.status, resp.reason 
    body = resp.read() 
    return body 

測試(與geventhttpclient):

def get_info(i): 
    url = URL('http://localhost:8001/simpletestaction/') 
    http = HTTPClient.from_url(url, concurrency=100, connection_timeout=60, network_timeout=60) 
    try: 
     response = http.get(url.request_uri) 
     s = response.status_code 
     body = response.read() 
    finally: 
     http.close() 


dt_start = dt.now() 
print 'Start: %s' % dt_start 

threads = [gevent.spawn(get_info, i) for i in xrange(401)] 
gevent.joinall(threads) 
dt_end = dt.now() 

print 'End: %s' % dt_end 
print dt_end-dt_start 

在這兩種情況下,我都有類似的時間。在類似的問題(API代理)中,gevent/greenlets和協作式多任務的優點是什麼?

回答

5

併發40並不是讓gevent閃耀的水平。 Gevent是關於併發性而不是並行性(或每請求性能),因此具有這種「低」的併發性水平並不是獲得改進的好方法。

通常你會看到GEVENT併發有成千上萬的水平,而不是40 :)

對於阻塞I/O蟒蛇線程不壞(GIL的期間I/O發佈),GEVENT的優勢在資源使用方面(有1000條python線程會過度殺傷),並且不需要考慮鎖定和朋友。

很明顯,請記住,您的整個應用程序必須對gevent友好以獲得優勢,而django(默認情況下)需要一點調整(因爲必須更改gevent友好的示例數據庫適配器)。

+0

我想用另一個線程/ greenlets來測試它。不是數千,而是數百。和結果類似。 我認爲如果我的控制器操作中有多個請求(使用join()/ joinall()),gevent是最好的選擇。 但在我的問題(「代理」-API)我沒有顯着的好處。 在第一種情況下(線程)我們有一個簡單的配置:-threads N. 在第二種情況下(gevent),我們在修補postgres驅動程序(例如),redis等時遇到了很多問題。另外,一個完整的堆棧跟蹤的問題... – OLMER

+0

對不起,不知道跟着你,如果你不能「補丁」django,你不能使用gevent,那就是。你的應用程序必須是100%非阻塞的,否則它是一個阻止應用程序,gevent不會幫助你(好吧,它甚至會做得最差) – roberto

1

服務非阻塞不是關於性能,而是關於併發性。如果請求時間的99%用於子請求中,則不能只優化這99%。但是,當所有可用線程忙於服務時,新客戶端將被拒絕,儘管99%的線程時間用於等待子請求完成。非阻塞服務允許您通過在不受可用線程數量限制的「處理程序」之間共享該空閒時間來利用該空閒時間。因此,如果99%正在等待,那麼另外1%是CPU限制的處理,因此在最大化CPU之前可以同時擁有100倍以上的連接數 - 而不需要100倍以上的線程,這可能太昂貴(並且使用Python的GIL問題,你必須使用更昂貴的子流程)。

現在,正如roberto所說,你的代碼必須是100%非阻塞才能挽救空閒時間。但是,從上面的百分比示例中可以看出,只有在請求幾乎完全IO限制時纔會變得非常重要。如果是這種情況,那麼很可能你不需要Django,至少對於你的應用程序的那部分來說。