2015-01-11 2 views
5

우선, 내 나쁜 영어로 유감스럽게 생각합니다. 내 프로젝트에는 많은 I/O 네트워크 요청이 있습니다. 주요 데이터는 다른 프로젝트에 저장되며 액세스는 웹 API (JSON/XML), 폴링에 의해 제공됩니다. 새로운 사용자 세션마다이 API를 사용합니다 (사용자에 대한 정보 얻기). 때로는 응답을 기다리는 데 문제가 있습니다. 우리는 nginx + uwsgi + django를 사용합니다. 아시다시피, Django는 동기식 (또는 블로킹)입니다. 대기중인 네트워크 IO 문제를 해결하기 위해 멀티 스레딩과 함께 uwsgi를 사용합니다. 나는 gevent에 관해 읽는 것을 결심했다. 협동과 선점 멀티 태스킹의 차이점을 이해합니다. 그리고이 문제 (네트워크 I/O 병목 현상)에 대해 gevent가 uwsgi 스레드보다 나은 솔루션이되기를 바랍니다. 그러나 그 결과는 거의 동일했다. 때때로 gevent는 약했다. 어딘가 잘못되었습니다. 말해줘. 제발.gevent 대 스레드가있는 Uwsgi

여기에 uwsgi 구성 예제가 있습니다. Gevent :

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

스레딩 :

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

예 컨트롤러 (geventhttpclient)와

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 

시험 :

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는 병렬 처리 (또는 요청 당 성능)가 아닌 동시성에 관한 것이므로 "낮은"동시성 수준을 유지하는 것이 개선을위한 좋은 방법이 아닙니다.

일반적으로 있습니다 (GIL은 I/O시 해제), gevent의 장점은 나쁘지 않아하지 40 :

I/O 파이썬 스레드를 차단, 수천의 수준 gevent 동시성을 볼 수 있습니다 리소스 사용 (1000 파이썬 스레드가 과잉 될 것입니다) 및 잠금 및 친구에 대해 생각할 필요가 없어졌습니다.

분명히 이점을 얻으려면 전체 앱을 피해야합니다. 기본적으로 django에는 약간의 튜닝이 필요합니다 (예를 들어, 데이터베이스 어댑터는 친숙한 방식으로 변경해야합니다).

+0

다른 수의 thread/greenlets로 테스트하려고합니다. 수천이 아니라 수백. 결과도 비슷합니다. 컨트롤러 동작에서 join()/joinall()을 사용하여 둘 이상의 요청이있는 경우 gevent가 가장 좋은 선택이라고 생각합니다. 그러나 문제 ("프록시"-API)에서 중요한 이점은 없습니다. 첫 번째 경우 (스레드)에는 간단한 구성이 있습니다. --threads N. 두 번째 경우 (gevent)에는 postgres 드라이버 (예 :), redis 등을 패치하는 데 많은 문제가 있습니다. 또한 전체 스택 추적 문제 ... – OLMER

+0

django를 "패치"할 수 없다면 죄송합니다, 당신을 따라 오지 못할 수도 있습니다. 당신은 gevent를 사용할 수 없습니다. 귀하의 애플 리케이션은 100 % non-blocking이어야합니다. 그렇지 않으면 애플 리케이션을 차단하고 gevent가 도움이되지 않습니다 (글쎄, 최악의 경우도 할 것입니다) – roberto

1

검색 비 블로킹은 성능에 관한 것이 아니라 동시성에 관한 것입니다. 요청 시간의 99 %가 하위 요청에 소비 된 경우 99 %를 최적화 할 수 없습니다. 그러나 사용 가능한 모든 스레드가 사용량이 많아지면 새 클라이언트는 거부되며 스레드의 99 %가 하위 요청 완료 대기에 소비됩니다. 비 차단 서비스를 사용하면 더 이상 사용 가능한 스레드 수에 의해 제한되지 않는 "처리기"간에 공유하여 유휴 시간을 활용할 수 있습니다. 따라서 99 %가 대기 중이면 다른 1 %는 CPU 바운드 처리이므로 CPU를 최대로 사용하기 전에 100 배 더 많은 연결을 가질 수 있습니다. 100 배 이상의 스레드가 없어도 너무 비쌀 수 있습니다 (Python의 GIL 문제는 훨씬 더 비싼 하위 프로세스를 사용해야 함).

이제 로버트는 유휴 시간을 복구 할 수 있으려면 코드가 100 % 비 차단이어야한다고 말했습니다. 그러나 위의 백분율 예제에서 볼 수 있듯이 요청이 거의 완전히 IO 바인딩 된 경우에만 중요하게됩니다.그렇다면 적어도 앱의 일부에서는 장고가 필요하지 않을 가능성이 높습니다.

관련 문제