2013-08-21 4 views
1

백엔드 서버로 병렬 처리를 가져와야하는 경우를 생각해보십시오.토네이도 gen.Task/gen.coroutine 데코레이터를 사용하여 병렬 처리를 수행하는 방법

5 개의 서로 다른 쿼리에 대해 N ELB를 쿼리하고 웹 클라이언트에 결과를 다시 보냅니다.

백엔드는 토네이도입니다. 이전에 docs에서 여러 번 읽은 바에 따르면, @ genTask 또는 gen.coroutine을 사용하면 과거에 여러 작업을 병렬로 처리 할 수있게되었습니다.

그러나 내 모든 요청 (20 자리 숫자, 4 엘브 * 5 쿼리)이 하나씩 처리되므로 여기에 뭔가 빠져 있어야합니다.

def query_elb(fn, region, elb_name, period, callback): 
    callback(fn (region, elb_name, period)) 

class DashboardELBHandler(RequestHandler): 

    @tornado.gen.coroutine 
    def get_elb_info(self, region, elb_name, period): 
     elbReq = yield gen.Task(query_elb, ELBSumRequest, region, elb_name, period) 
     elb2XX = yield gen.Task(query_elb, ELBBackend2XX, region, elb_name, period) 
     elb3XX = yield gen.Task(query_elb, ELBBackend3XX, region, elb_name, period) 
     elb4XX = yield gen.Task(query_elb, ELBBackend4XX, region, elb_name, period) 
     elb5XX = yield gen.Task(query_elb, ELBBackend5XX, region, elb_name, period) 

     raise tornado.gen.Return( 
      [ 
       elbReq, 
       elb2XX, 
       elb3XX, 
       elb4XX, 
       elb5XX, 
      ] 
     ) 

    @tornado.web.authenticated 
    @tornado.web.asynchronous 
    @tornado.gen.coroutine 
    def post(self): 
     ret = [] 

     period = self.get_argument("period", "5m") 

     cloud_deployment = db.foo.bar.baz() 
     for region, deployment in cloud_deployment.iteritems(): 

      elb_name = deployment["elb"][0] 
      res = yield self.get_elb_info(region, elb_name, period) 
      ret.append(res) 

     self.push_json(ret) 



def ELBQuery(region, elb_name, range_name, metric, statistic, unit): 
    dimensions = { u"LoadBalancerName": [elb_name] } 

    (start_stop , period) = calc_range(range_name) 

    cw = boto.ec2.cloudwatch.connect_to_region(region) 
    data_points = cw.get_metric_statistics(period, start, stop, 
     metric, "AWS/ELB", statistic, dimensions, unit)  

    return data_points 

ELBSumRequest = lambda region, elb_name, range_name : ELBQuery(region, elb_name, range_name, "RequestCount", "Sum", "Count") 
ELBLatency  = lambda region, elb_name, range_name : ELBQuery(region, elb_name, range_name, "Latency", "Average", "Seconds") 
ELBBackend2XX = lambda region, elb_name, range_name : ELBQuery(region, elb_name, range_name, "HTTPCode_Backend_2XX", "Sum", "Count") 
ELBBackend3XX = lambda region, elb_name, range_name : ELBQuery(region, elb_name, range_name, "HTTPCode_Backend_3XX", "Sum", "Count") 
ELBBackend4XX = lambda region, elb_name, range_name : ELBQuery(region, elb_name, range_name, "HTTPCode_Backend_4XX", "Sum", "Count") 
ELBBackend5XX = lambda region, elb_name, range_name : ELBQuery(region, elb_name, range_name, "HTTPCode_Backend_5XX", "Sum", "Count") 
+1

'ELBSumRequest'와 친구들이 coroutine을 만드는 대신 함수를 차단하고 있다면, 토네이도가 인터리브 할 수있는 방법이 없습니다.그것들을 다시 쓸 수 없다면 (또는 작은 조각으로 나눠서 각자가 다음에 내놓을 수있게되면, 스케줄러에게 중간에 들어갈 기회가 주어집니다), 코 루틴은 여기 당신을 도울 수 없습니다; 당신은 스레드를 사용해야합니다 (또는 호출 차단 monkeypatching에 의해 준 선제 적 그린 레트를 사용해야합니다. 그러나 토네이도는 후자를 위해서가 아니라 후자를 위해 지원합니다. 그리고 나는 당신이'gevent' 또는 무엇이든 모든 것을 다시 쓰길 원합니다.). – abarnert

+0

@abarnert, 방금 구현을 추가했습니다. ELBQuery를 비동기식으로 재 작성하는 방법을 돕고 제안 해 주시겠습니까? –

답변

2

문제는 ELBQuery이 차단 기능입니다. 어딘가에서 yield 다른 coroutine이 없다면, 콜 루틴 스케줄러가 호출을 인터리빙 할 수있는 방법이 없습니다. (코 루틴의 핵심은 협조적이며 선제 적이 지 않습니다.)

문제가 calc_range 호출과 비슷하면 처리하기 쉽지만 작은 조각으로 나누면됩니다 다음으로, 스케줄러에게 각 조각 사이에 들어갈 수있는 기회를줍니다.

그러나 대부분 차단하는 호출은 boto이며, 함수의 대부분의 시간은 get_metric_statistics이 돌아 오기를 기다리는 데 사용되지만 다른 것은 실행할 수 없습니다.

그래서 어떻게 해결할 수 있습니까?

  1. 각 boto 작업마다 스레드를 스핀 오프하십시오. 토네이도는 스레드 또는 스레드 풀 작업에 대해 코 루틴을 투명하게 감싸는 것이 매우 쉽습니다. 모든 작업이 마술처럼 차단됩니다. 물론 스레드를 사용하는 데 비용이 들게됩니다.
  2. 스레드 풀 대신 스레드 풀에서 boto 작업을 예약하십시오. # 1과 비슷한 절충점, 특히 소수의 작업 만있는 경우. (그러나 500 명의 다른 사용자를 위해 5 개의 작업을 수행 할 수 있다면 공유 풀을 원할 것입니다.)
  3. coroutines를 사용하려면 boto를 다시 작성하거나 monkeypatch하십시오. 이것은 이상적인 솔루션이 될 것입니다 ...하지만 그것은 대부분의 작업입니다. 그리고 당신이 이해하지 못하는 코드를 깨뜨리고 보토 업데이트 등으로 유지해야하는 가장 큰 위험이 있습니다. 그러나 최소한이 프로젝트를 시작한 사람이 있습니다 (예 : asyncboto 프로젝트).
  4. greenlets을 사용하고 라이브러리의 종속성에 대한 충분한 monkeypatch을 사용하면 비동기로 속일 수 있습니다. 이것은 해커처럼 들리지만 실제로는 최상의 솔루션 일 수 있습니다. 이에 대해서는 Marrying Boto to Tornado을 참조하십시오.
  5. greenlets을 사용하고 stdlib ala gevent 전체를 monkeypatch하면 boto와 토네이도가 함께 작동하지 않고 함께 작동합니다. 이것은 끔찍한 생각처럼 들립니다. 전체 앱을 gevent으로 이식하는 것이 좋습니다.
  6. gevent과 같은 것을 사용하는 별도의 프로세스 (또는 그 풀)를 사용하십시오.

자세한 내용을 알지 못하면서 # 2와 # 4를 먼저 보는 것이 좋겠지 만, 그들이 당신에게 가장 좋은 대답으로 밝혀 지겠다고 약속 할 수는 없습니다.

+0

내가 한 번에 하나의 boto 요청을 수행 할 tornado.web.RequestHandler를 작성하고 async_http_client (내부적으로)를 사용하여 호출하는 방법을 확인하고 싶습니다. –

+0

@ TzuryBarYochay : 단지 각 boto 호출이 원래 RequestHandler를 차단하고 있음을 의미합니다. 따라서 핸들러를 스레드하거나 다른 방법으로 동기화하지 않으면 큰 도움이되지 않습니다. (토네이도 서버 프로세스를 사용하여 문제를 해결할 수는 있지만, 토네이도는 실제로 당신을 위해 아무것도하지 않습니다. 모든 boto 자료를 동 기적으로 쉽게 작성할 수 있습니다. – abarnert

+0

감사합니다. greenlets와 monkeypatch 일을 시도하십시오. –

관련 문제