2016-09-20 53 views
0

我正在研究一个涉及向api提出许多请求的项目,并且我正在做出决定并保存在db中的每个反馈。我正在使用adbapi与mysql进行通信。长延期任务的扭曲反应器块

我正在接收请求作为POST,其中包含要推送到远程API并保存的项目列表。

我已经注意到,虽然处理延期的所有其他操作块中的项目,直到一部分完成。

下面是一个例子,显示了类似于我正在做的事情。

#!/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() 

在上面的例子中,当我有大量的电子邮件列表中的像100000当我做send_mail它会阻止其他操作,直至其完成。如果我在发生这种情况时尝试发送另一个请求,则会在完成后阻止它。

我的问题是,有没有办法让我的操作能够同时进行?我可以发送邮件并以并发方式save_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得到结果,以便我可以使用它。我注意到延期邮件等待所有邮件发送。我想要一种方式来处理每个邮件并保存在分贝,而不是等待所有。 –

+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”吗?回答:是和不是。你不能同时这样做,因为据我所知,保存数据库中的数据需要邮件发送的一些结果。但是,如果您的意思是:只要获得第一个邮件结果,我就可以开始将数据保存在数据库中,而不用等到数据库中存储所有邮件结果时再发送 - 是的,您可以这样做;只是结合您的两个处理功能于一体:

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延迟的全部意义在于延迟是立即返回而没有阻塞,而当您执行某些事件时,与它们关联的回调将在稍后触发。如果ANYWING阻止任何地方,Twisted无能为力 - 它是单线程的,基本上是一个多任务协作。但Twisted不能将你的代码变成非阻塞的魔法 - 你必须这样做。

旁注:你使用server.NOT_DONE_YET的方式是毫无意义的。只需将“Received”作为字符串返回并忘记请求对象。您在其他地方调用request.finish()时不会立即使用NOT_DONE_YET。

+0

艾伦,我没有在我的代码中做任何阻塞呼叫。 send_mail和save_in_db都没有阻塞调用返回一个被攻击者。正如我所说的,当请求很多时(比如50k请求),问题就会被注意到。我已经编辑了我的代码,只要我从send_mail函数获得响应时就开始保存,但我仍然注意到save方法只会在send_mail的所有请求都被延迟后才开始,这可能需要很长时间在那段时间内反应堆没有做任何其他事情。 –

+0

关于NOT_DONE_YET,当我没有返回它时(例如像你所建议的那样返回'Received'),我得到一个异常('requests.finish被调用后调用请求时调用'exceptions.RuntimeError:Request.write' )。它是如何工作的? –

+0

是的。你不应该对请求做任何事情。没有写(),没有完成() - 只是返回“收到”。 –