我正在开展一个涉及向api发出许多请求的项目,以及每个反馈我正在做出决定并保存在数据库中。我正在使用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()
在上面的示例中,当我在列表中有大量电子邮件时(例如100,000),当我正在执行send_mail
时,它会阻止其他操作直到完成。如果我在发生这种情况时尝试发送另一个请求,它会在完成后阻塞。
我的问题是,有没有办法让操作同时发生?我可以send_mail并以并发的方式save_in_db吗?我可以这样做,因为我收到其他请求和处理而不必等待彼此完成?
答案 0 :(得分: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"
答案 1 :(得分:0)
我过去使用的一个技巧是inlineCallbacks
和yield
的组合。基本上,您可以迭代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)
您必须调整间隔值以满足您的需求,因为该值取决于结果的产生速度。希望这会有所帮助。
答案 2 :(得分: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延迟的全部内容是延迟会立即返回而不会阻塞,而与之关联的回调会在以后执行某些操作时触发。如果ANYTHING阻止任何地方,Twisted什么都不做 - 它是单线程的,基本上是一个合作的多任务处理。但Twisted无法将你的代码神奇地变成非阻塞 - 你必须这样做。
旁注:您使用server.NOT_DONE_YET的方式毫无意义。只需将“已接收”作为字符串返回并忘记请求对象。在其他地方调用request.finish()时,你使用NOT_DONE_YET,而不是立即。