Python 将芹菜用作扭曲应用程序的控制通道
我试图使用芹菜作为扭曲应用程序的控制通道。我的Twisted应用程序是一个抽象层,它为各种本地运行的进程(通过ProcessProtocol)提供了一个标准接口。我想用芹菜来远程控制它——AMQP似乎是从一个中心位置控制许多Twisted应用程序的理想方法,我想利用芹菜的基于任务的功能,例如任务重试、子任务等 这并没有像我计划的那样有效,我希望有人能帮我找到正确的方向,让这一切顺利进行 当我运行脚本时,我试图实现的行为是:Python 将芹菜用作扭曲应用程序的控制通道,python,twisted,celery,Python,Twisted,Celery,我试图使用芹菜作为扭曲应用程序的控制通道。我的Twisted应用程序是一个抽象层,它为各种本地运行的进程(通过ProcessProtocol)提供了一个标准接口。我想用芹菜来远程控制它——AMQP似乎是从一个中心位置控制许多Twisted应用程序的理想方法,我想利用芹菜的基于任务的功能,例如任务重试、子任务等 这并没有像我计划的那样有效,我希望有人能帮我找到正确的方向,让这一切顺利进行 当我运行脚本时,我试图实现的行为是: 开始一种稍加修饰的芹菜(参见 (见下文) 等待芹菜任务 收到“启动流程
- 开始一种稍加修饰的芹菜(参见 (见下文)
- 等待芹菜任务
- 收到“启动流程”任务后,生成一个ProcessProtocol
- 当收到其他任务时,在Twisted协议上运行一个函数,并使用Deferreds返回结果
- StartProcTask生成进程,并通过一个不同的
- CountWordsTask从未收到结果,因为从未调用childDataReceived
芹菜可能在等待来自网络的新消息时阻塞。由于您在一个单线程进程中与Twisted reactor一起运行它,因此它会阻止reactor运行。这将禁用大多数Twisted,这需要反应堆实际运行(您调用了
reactor.run
,但芹菜阻塞了它,它实际上没有运行)
reactor.callLater
只会延迟芹菜的启动。一旦芹菜开始生长,它仍然会阻塞反应堆
你需要避免的问题是阻塞反应堆
一种解决方案是在一个线程中运行芹菜,在另一个线程中运行反应器。使用reactor.callFromThread
从芹菜线程向Twisted(“reactor线程中的调用函数”)发送消息。如果您需要从扭曲的线程向芹菜发送消息,请使用芹菜等价物
另一个解决方案是将芹菜协议(AMQP?-see)实现为本机Twisted库,并使用它来处理芹菜消息而不阻塞。感谢你们两位花时间响应。哦,按下enter键太早了。谢谢你们两位花时间回复。你介意告诉我用这个方法从芹菜的线程中使用spawnProcess的最佳方法吗?我也需要通过callFromThread调用它吗?我已经使用txAMQP对此进行了概念验证,但我发现自己正在尝试复制芹菜的大部分功能,所以我想我会尝试直接从Twisted内部使用芹菜。这只是对将来尝试做类似事情的其他人的一个快速跟进。基于上述建议,我通过使用
reactor.callInThread(worker.execute\u from\u commandline)
启动芹菜,然后从芹菜任务代码中调用blockingCallFromThread(reactor,reactor.spawnProcess,*args,**kwargs)
。我几乎让它完全按照计划工作——还有一个不同的问题需要解决,我已经准备好了。再次感谢Jean Paul和Glyph的帮助!为了完整起见,现在有一个在线程中运行Twisted reactor的库
from functools import partial
from celery.app import App
from celery.bin.celeryd import WorkerCommand
from twisted.internet import reactor
class MyCeleryApp(App):
def __init__(self, twisted, *args, **kwargs):
self.twisted = twisted
super(MyCeleryApp, self).__init__(*args, **kwargs)
def main():
get_my_app = partial(MyCeleryApp, reactor)
worker = WorkerCommand(get_app=get_my_app)
reactor.callLater(1, worker.execute_from_commandline)
reactor.run()
if __name__ == '__main__':
main()
from twisted.internet import protocol
from twisted.internet.defer import Deferred
class WCProcessProtocol(protocol.ProcessProtocol):
def __init__(self, text):
self.text = text
self._waiting = {} # Dict to contain deferreds, keyed by command name
def connectionMade(self):
if 'startup' in self._waiting:
self._waiting['startup'].callback('process started')
def outReceived(self, data):
fieldLength = len(data) / 3
lines = int(data[:fieldLength])
words = int(data[fieldLength:fieldLength*2])
chars = int(data[fieldLength*2:])
self.transport.loseConnection()
self.receiveCounts(lines, words, chars)
if 'countWords' in self._waiting:
self._waiting['countWords'].callback(words)
def processExited(self, status):
print 'exiting'
def receiveCounts(self, lines, words, chars):
print >> sys.stderr, 'Received counts from wc.'
print >> sys.stderr, 'Lines:', lines
print >> sys.stderr, 'Words:', words
print >> sys.stderr, 'Characters:', chars
def countWords(self, text):
self._waiting['countWords'] = Deferred()
self.transport.write(text)
return self._waiting['countWords']
from celery.task import Task
from protocol import WCProcessProtocol
from twisted.internet.defer import Deferred
from twisted.internet import reactor
class StartProcTask(Task):
def run(self):
self.app.proc = WCProcessProtocol('testing')
self.app.proc._waiting['startup'] = Deferred()
self.app.twisted.spawnProcess(self.app.proc,
'wc',
['wc'],
usePTY=True)
return self.app.proc._waiting['startup']
class CountWordsTask(Task):
def run(self):
return self.app.proc.countWords('test test')