队列远程调用Python Twisted透视代理?

时间:2010-05-18 23:22:21

标签: python asynchronous twisted

Twisted(对于python)的优势是它的异步框架(我认为)。我写了一个图像处理服务器,通过Perspective Broker接收请求。只要我一次喂它少于几百张图像,它就能很好地工作。然而,有时它几乎同时会被数百张图像加入。因为它试图同时处理它们,所以服务器崩溃。

作为一种解决方案,我想在服务器上排队remote_calls,这样它一次只能处理~100张图像。看起来这可能是Twisted已经做过的事情,但我似乎无法找到它。关于如何开始实施这个的任何想法?正确方向的一点?谢谢!

2 个答案:

答案 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也有明确的acquirerelease方法,但run方法非常方便,几乎总是你想要的。它调用acquire方法,该方法返回Deferred。对于第一个Deferred,它添加了一个回调,它调用您传入的函数(以及任何位置或关键字参数)。如果该函数返回Deferred,那么到第二个Deferred会添加一个调用release方法的回调。

通过立即调用release来处理同步案例。错误也通过允许它们传播来处理,但确保完成必要的release以使DeferredSemaphore保持一致状态。传递给run的函数的结果(或它返回的Deferred的结果)将成为Deferred返回的run的结果。

另一种可能的方法可能基于DeferredQueuecooperateDeferredQueue大致类似于普通队列,但其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个实例,该实例包含CooperativeTaskpause和其他几个有用的方法。此外,分配给同一合作者的所有作业将在调度中彼此合作,以免过载事件循环(这就是API的名称)。在resume方面,还可以对待处理项目的数量设置限制,这样可以避免服务器完全过载(例如,如果图像处理器卡住并停止完成任务)。如果调用DeferredQueue的代码处理队列溢出异常,您可以使用此作为压力来尝试停止接受新作业(可能将它们分流到另一台服务器,或警告管理员)。使用put做类似的事情有点棘手,因为没有办法限制等待能够获取信号量的作业数量。

答案 1 :(得分:-2)

你可能也喜欢我写的txRDQ(Resizable Dispatch Queue)。谷歌它,它在LaunchPad的tx系列中。对不起,我没有更多时间回复 - 即将上台。

特里