2016-09-20 8 views
0

나는 api에 대한 많은 요청을하는 것과 관련된 프로젝트에 참여하고 있으며 각각의 피드백에 대해 결정을 내리고 db에 저장하고 있습니다. 나는 mysql과 통신하기 위해 adbapi를 사용하고있다.긴 지연된 작업을위한 뒤틀린 리액터 블록

원격 API에 푸시되고 저장되는 항목 목록이 포함 된 POST 요청을 수신하고 있습니다.

나는 한 부분이 완료 될 때까지 다른 모든 작업 블록을 연기 한 항목을 처리하는 동안 언급했다.

다음은 내가하는 것과 비슷한 것을 보여주는 예입니다. 위의 예에서

#!/usr/bin/python2.7 

from twisted.web.server import Site 
from twisted.web.resource import Resource 
from twisted.internet import reactor, defer 
from twisted.web.server import NOT_DONE_YET 

from utils import send_mail, save_in_db 


def get_params(request): 
    params = {} 
    for k, v in request.args.items(): 
     if k and v: 
      params[k] = v[0] 
    return params 


class SendPage(Resource): 

    def render_POST(self, request): 
     params = get_params(request) 
     emails = params['emails'] 
     message = params['message'] 
     self.process_send_mail(message, emails) 
     request.write('Received') 
     request.finish() 
     return NOT_DONE_YET 

    def process_send_mail(self, message, emails): 
     defs = [] 
     for email in emails: 
      d = send_mail(email, message) 
      defs.append(d) 
     d1 = defer.DeferredList(defs) 
     d1.addCallback(self.process_save) 

    def process_save(self, result): 
     defs = [] 
     for r in result: 
      d = save_in_db(r) 
      defs.append(d) 
     d1 = defer.DeferredList(defs) 
     d1.addCallback(self.post_save) 

    def post_save(self, result): 
     print "request was completed" 


root = Resource() 
root.putChild("", SendPage()) 
factory = Site(root) 
reactor.listenTCP(8880, factory) 
reactor.run() 

, 나는 그것의 완성까지 send_mail 그것은 블록 다른 작업을하고있는 중이 야 때 나는 10 만 같은 목록에 많은 전자 우편이있을 때. 그 일이 일어나는 동안 다른 요청을 보내려고하면 그 요청이 완료 될 때까지 차단됩니다.

제 질문은, 동시에 작업을 수행 할 수있는 방법이 있습니까? send_mail과 send_in_db를 동시에 사용할 수 있습니까? 서로 다른 요청을 받고 서로 기다릴 필요없이 처리 할 수 ​​있습니까?

답변

0

당신은 결과를 기다리는 생략하거나 모든 결과를 기다릴 수 있습니다 보내고과 같이 데이터베이스에 저장 : 나는 과거에 활용 한

def process_send_mail(self, message, emails): 
    defs = [] 
    for email in emails: 
     d = send_mail(email, message) 
     defs.append(d) 
     d = save_in_db(email) 
     defs.append(d) 

    d1 = defer.DeferredList(defs) 
    d1.addCallback(self.post_save)  

def post_save(self): 
    print "request was completed" 
+0

그러나'for r in result'에서 루핑 할 결과는 위에 정의되지 않았습니까? send_mail에서 결과를 얻어야 만 사용할 수 있습니다. 지연 메일은 모든 메일이 전송되기를 기다리는 것으로 나타났습니다. 각 메일을 처리하고 모두를 기다리는 대신 DB에 저장하는 방법이 필요합니다. –

+0

고정. 'send_mail'을 반환하는 것이 무엇인지 모르겠습니다. 그것을 사용하여 데이터베이스에 전달합니다. 'save_in_db'에 전달할 매개 변수라고 가정합니다. –

0

한 트릭은 inlineCallbacksyield의 조합입니다. 기본적으로 반응기가 다른 작업을 수행 할 수 있도록 n 개의 요소를 반복하고 yield 또는 특정 간격으로 일시 중지 할 수 있습니다. 따라서 귀하의 경우 루프를 잠글 수있는 모든 기능을 @inlineCallbacks, enumerate 루프로 장식 한 다음 특정 지점에서 yield/일시 중지를 사용하여 제어 장치를 다시 원자로에 제공 할 수 있습니다.

@defer.inlineCallbacks 
def process_send_mail(self, message, emails): 
    defs = [] 
    for i, email in enumerate(emails): # enumerate 
     d = send_mail(email, message) 
     defs.append(d) 
     if i % 1000 == 0: 
      yield # pause every 1000 elements 
    d1 = defer.DeferredList(defs) 
    d1.addCallback(self.process_save) 

결과가 얼마나 빨리 생성되는지에 따라 값이 달라 지므로 필요에 맞게 간격 값을 조정해야합니다. 희망이 도움이됩니다.

0

실제로 두 가지 질문이 있습니다. 나는 그것들을 개별적으로 다룰 것이다.

첫 번째 내용은 "작업이 동시에 발생할 수 있습니까? send_mail과 동시에 save_in_db로 처리 할 수 ​​있습니까?"

대답은 예와 아니오입니다. 데이터를 DB에 저장하는 것이 메일 전송의 결과를 필요로하기 때문에 동시에 처리 할 수 ​​없습니다. 그러나 만약 당신이 의미했다 : DB에 물건을 저장하기 전에 모든 메일 결과가 오기를 기다리지 않고 첫 번째 메일 결과를 얻는 즉시 DB에 저장을 시작할 수 있습니까? 네, 그렇게 할 수 있습니다. 하나에 당신이 개 처리 기능을 결합 :

def process_send_mail_and_save(self, message, emails): 
    defs = [] 
    for email in emails: 
     d = send_mail(email, message) 
     # might require tuning for save_in_db parameters if not matching send_mail callback output 
     d.addCallback(save_in_db) 
     defs.append(d) 
    d1 = defer.DeferredList(defs) 
    d1.addCallback(self.post_save) 

2) "나는 다른 요청을 받아 처리로 그렇게 할 수 있습니다 완료 될 때까지 서로를 위해 기다릴 필요없이?"

물론 Twisted에서 할 수 있습니다. 그러나 올바른 방법으로 코드를 작성해야합니다.당신은 send_mail이나 save_in_db가하는 일을 우리에게 말하지 않습니다 - 당신이 쓰는 것 같아요. 그 기능이 차단되어 대부분의 이슈를 야기한다고 생각합니다. - 아마도 send_mail은 모든 SMTP 작업을 끝냅니다. 그것은 돌아온다? 그것은 바로 연기 반환해야하고, 작업이 완료 콜백 :

http://twistedmatrix.com/documents/16.4.0/core/howto/clients.html

나는 당신이 send_mail 및 save_in_db 기능의 주위에 타임 스탬프로 전화를 로깅 넣어 제안 - 순간 당신이 그들에게 전화하지 순간 주위에 자신의 지연된 화재.

기억해 두십시오 : Twisted의 지연 점은 지연이 즉시 블로킹되지 않고 콜백이 사용자에게 연결되는 동안 나중에 무언가가 실행될 때 연기된다는 것입니다. 어딘가에 어떤 블록이 있어도 Twisted는 아무 것도 할 수 없습니다. 이것은 단일 스레드이며, 기본적으로 협력적인 멀티 태스킹입니다. Twisted는 코드를 마술처럼 비 차단으로 만들 수 없습니다.

사이드 노트 : server.NOT_DONE_YET을 (를) 사용하는 방식은 무의미합니다. "Received"를 문자열로 반환하고 요청 객체를 잊어 버리십시오. 즉시 request.finish()를 호출 할 때 NOT_DONE_YET을 사용합니다.

+0

Allan, 내 코드에서 차단 호출을하고 있지 않습니다. send_mail과 save_in_db 모두 블로킹 호출을 수행하지 않고 있습니다. 내가 말했듯이 요청이 많은 경우 (50k 요청과 같이) 문제가 언급됩니다. send_mail 함수의 응답을받는 즉시 저장 시작 위치를 제안하는 방식으로 코드를 편집했지만 send_mail에 대한 모든 요청이 지연된 후에 만 ​​save 메서드가 시작된다는 점에 유의했습니다. 이는 상당한 시간이 걸릴 수 있습니다. 그 시간 동안 원자로는 다른 일을하지 않습니다. –

+0

NOT_DONE_YET에 대한 귀하의 부업위에서, 귀하가 제안한 것처럼 'Received'를 반환하는 등의 경우, 예외가 발생합니다 ('exceptions.RuntimeError : Request.finish가 호출 된 후 Request.write가 요청됩니다 .').). 어떻게 작동해야합니까? –

+0

예. 요청으로 아무 것도하지 않아야합니다. write(), finish()가 없습니다. 그냥 "Received"를 반환합니다. –