Twisted(对于python)的优势是它的异步框架(我认为)。我写了一个图像处理服务器,通过Perspective Broker接收请求。只要我一次喂它少于几百张图像,它就能很好地工作。然而,有时它几乎同时会被数百张图像加入。因为它试图同时处理它们,所以服务器崩溃。
作为一种解决方案,我想在服务器上排队remote_calls,这样它一次只能处理~100张图像。看起来这可能是Twisted已经做过的事情,但我似乎无法找到它。关于如何开始实施这个的任何想法?正确方向的一点?谢谢!
答案 0 :(得分:29)
可能对此有帮助的一个现成选项是twisted.internet.defer.DeferredSemaphore
。这是您可能已经知道的正常(计数)信号量的异步版本,如果您已经完成了很多线程编程。
(计数)信号量很像互斥锁(锁定)。但是,只有在相应的版本中才能获取一次互斥锁,可以配置一个(计数)信号量,以允许任意(但指定)数量的采集在需要任何相应的版本之前成功。
以下是使用DeferredSemaphore
运行10个异步操作但最多同时运行3个异步操作的示例:
from twisted.internet.defer import DeferredSemaphore, gatherResults
from twisted.internet.task import deferLater
from twisted.internet import reactor
def async(n):
print 'Starting job', n
d = deferLater(reactor, n, lambda: None)
def cbFinished(ignored):
print 'Finishing job', n
d.addCallback(cbFinished)
return d
def main():
sem = DeferredSemaphore(3)
jobs = []
for i in range(10):
jobs.append(sem.run(async, i))
d = gatherResults(jobs)
d.addCallback(lambda ignored: reactor.stop())
reactor.run()
if __name__ == '__main__':
main()
DeferredSemaphore
也有明确的acquire
和release
方法,但run
方法非常方便,几乎总是你想要的。它调用acquire
方法,该方法返回Deferred
。对于第一个Deferred
,它添加了一个回调,它调用您传入的函数(以及任何位置或关键字参数)。如果该函数返回Deferred
,那么到第二个Deferred
会添加一个调用release
方法的回调。
通过立即调用release
来处理同步案例。错误也通过允许它们传播来处理,但确保完成必要的release
以使DeferredSemaphore
保持一致状态。传递给run
的函数的结果(或它返回的Deferred
的结果)将成为Deferred
返回的run
的结果。
另一种可能的方法可能基于DeferredQueue
和cooperate
。 DeferredQueue
大致类似于普通队列,但其get
方法返回Deferred
。如果在通话时队列中没有任何项目,则在添加项目之前Deferred
将不会触发。
以下是一个例子:
from random import randrange
from twisted.internet.defer import DeferredQueue
from twisted.internet.task import deferLater, cooperate
from twisted.internet import reactor
def async(n):
print 'Starting job', n
d = deferLater(reactor, n, lambda: None)
def cbFinished(ignored):
print 'Finishing job', n
d.addCallback(cbFinished)
return d
def assign(jobs):
# Create new jobs to be processed
jobs.put(randrange(10))
reactor.callLater(randrange(10), assign, jobs)
def worker(jobs):
while True:
yield jobs.get().addCallback(async)
def main():
jobs = DeferredQueue()
for i in range(10):
jobs.put(i)
assign(jobs)
for i in range(3):
cooperate(worker(jobs))
reactor.run()
if __name__ == '__main__':
main()
请注意,async
辅助功能与第一个示例中的功能相同。但是,这一次,还有一个worker
函数明确地将作业从DeferredQueue
中拉出来并使用async
处理它们(通过添加async
作为对{的回调由Deferred
返回的{1}}。 get
生成器由worker
驱动,在每个cooperate
产生火之后迭代它一次。然后,主循环启动其中三个工作器生成器,以便在任何给定时间执行三个作业。
这种方法涉及比Deferred
方法更多的代码,但有一些可能有趣的好处。首先,DeferredSemaphore
会返回cooperate
个实例,该实例包含CooperativeTask
,pause
和其他几个有用的方法。此外,分配给同一合作者的所有作业将在调度中彼此合作,以免过载事件循环(这就是API的名称)。在resume
方面,还可以对待处理项目的数量设置限制,这样可以避免服务器完全过载(例如,如果图像处理器卡住并停止完成任务)。如果调用DeferredQueue
的代码处理队列溢出异常,您可以使用此作为压力来尝试停止接受新作业(可能将它们分流到另一台服务器,或警告管理员)。使用put
做类似的事情有点棘手,因为没有办法限制等待能够获取信号量的作业数量。
答案 1 :(得分:-2)
你可能也喜欢我写的txRDQ(Resizable Dispatch Queue)。谷歌它,它在LaunchPad的tx系列中。对不起,我没有更多时间回复 - 即将上台。
特里