2015-01-11 13 views
5

Po pierwsze, przepraszam za mój zły angielski. W moim projekcie mam wiele żądań sieci I/O. Główne dane przechowywane w innych projektach, a dostęp zapewnia internetowy interfejs API (JSON/XML), polling. Używamy tych interfejsów API dla każdej nowej sesji użytkownika (uzyskiwanie informacji o użytkowniku). Czasami mamy problem z czekaniem na odpowiedź. Używamy nginx + uwsgi + django. Jak wiesz, Django jest synchroniczny (lub blokujący). Używamy uwsgi z wielowątkowością do rozwiązania problemu z siecią IO oczekiwania. Postanowiłem przeczytać o geenie. Rozumiem różnicę między wielozadaniowością opartą na współpracy i prewencją. Miałem nadzieję, że gevent jest lepszym rozwiązaniem niż wątki uwsgi dla tego problemu (wąskie gardło sieci I/O). Ale wyniki były niemal identyczne. Czasami geulacja była słabsza. Może gdzieś się mylę. Powiedz mi proszę.Uwsgi z gevent vs wątki

Oto przykłady konfiguracji wtsgi. Gevent:

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

przewlekania

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

Kontroler przykład:

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 

testu (z 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 

W obu przypadkach mam podobny czas. Jakie są zalety gevent/greenlets i kooperatywna wielozadaniowość w podobnym zagadnieniu (proxy API)?

Odpowiedz

5

Współczynnik równości 40 nie jest takim poziomem, aby pozwolić geentom świecić. Gevent dotyczy współbieżności, a nie równoległości (lub wydajności na żądanie), więc posiadanie takiego "niskiego" poziomu współbieżności nie jest dobrym sposobem na ulepszenia.

Generalnie widać gevent współbieżności z poziomu tysięcy, a nie 40 :)

Do blokowania I/O Pythona wątki nie są złe (GIL jest uwalniane podczas I/O), zaletą jest gevent w zużyciu zasobów (z 1000 wątków python będzie overkill) i usunięciem potrzeby myślenia o blokowaniu i znajomych.

Oczywiście, pamiętaj, że cała aplikacja musi być przyjazna dla geventów, aby uzyskać przewagę, a django (domyślnie) wymaga nieco dopasowania (jako przykładowe adaptery baz danych muszą być zmienione z czymś przyjaznym dla geventu).

+0

Próbuję przetestować go z inną liczbą wątków/greenletów. Nie tysiące, ale setki. A wynik jest podobny. Myślę, że gevent jest najlepszym wyborem, jeśli mam więcej niż jedno żądanie w mojej akcji kontrolera (używając join()/joinall()). Ale w moim wydaniu ("proxy" -API) nie mam znaczących korzyści. W pierwszym przypadku (wątki) mamy prosty configure: --threads N. W drugim przypadku (gevent) mamy duży problem z łataniem sterownika PostgreSQL (na przykład), redis itp. Mamy również problem ze śledzeniem całego stosu ... – OLMER

+0

Przykro nam, nie do końca Cię śledzić, jeśli nie możesz "załatać" django, nie możesz użyć gevent, thatsit. Twoja aplikacja musi być w 100% niezablokowana, w przeciwnym razie jest to aplikacja blokująca, a gevent nie pomoże ci (cóż, może nawet zrobić najgorsze) – roberto

1

Podanie nieblokowania nie dotyczy wydajności, chodzi o współbieżność. Jeśli 99% czasu żądania zostanie wydane na sub-żądanie, nie można po prostu zoptymalizować tych 99%. Ale gdy wszystkie dostępne wątki są zajęte, nowi klienci są odrzucani, chociaż 99% wątków jest poświęcanych na oczekiwanie na zakończenie żądania podrzędnego. Non-blocking serving pozwala ci wykorzystać ten czas bezczynności, dzieląc go pomiędzy "handlerami", których nie ogranicza liczba dostępnych wątków. Więc jeśli 99% czeka, to 1% to przetwarzanie związane z procesorem, dlatego możesz mieć 100x więcej połączeń jednocześnie, zanim osiągniesz maksymalną moc procesora - bez 100x więcej wątków, które mogą być zbyt kosztowne (i z GIL Pythona problem, trzeba użyć podprocesów, które są jeszcze droższe).

Teraz, jak powiedział roberto, twój kod musi być w 100% nieprzechowywany, aby móc odzyskać czas bezczynności. Jednak, jak widać z powyższego przykładu procentowego, staje się on krytyczny tylko wtedy, gdy żądania są prawie całkowicie związane z IO.Jeśli tak jest, prawdopodobnie nie potrzebujesz Django, przynajmniej w tej części aplikacji.