2009-12-22 94 views
3

我试图实现与扭曲的一种服务,是相当接近的“手指”教程这里找到:http://twistedmatrix.com/documents/current/core/howto/tutorial/intro.html扭曲的线程与子进程.Popen?

我有一个basic.LineListener等待命令,然后执行它,那么我有一个客户端连接和发布命令。麻烦的是,该命令有时需要执行其他的东西,我正在使用python的子进程模块。这不仅仅是沟通()的呼叫挂起,这是一个正常的子进程问题,我知道如何克服它。就是那个子进程.Popen调用是挂起的。

这里是扭曲的服务器代码:

from twisted.application import internet, service 
from twisted.internet import protocol, reactor, defer, threads 
from twisted.protocols import basic 
import sys 
import time 
import subprocess 

class MyProtocol(basic.LineReceiver): 
    def lineReceived(self, line): 
     self.go() 
    def go(self): 
     def writeResponse(message): 
      self.transport.write(message + '\r\n') 
      self.transport.loseConnection() 
     threads.deferToThread(self.factory.action).addCallback(writeResponse) 
    def connectionMade(self): 
     self.lines = [] 

class ActionService(service.Service): 
    def __init__(self, **kwargs): 
     pass 
     #self.users = kwargs 

def action(self): 
    print "launching subprocess" 
    sys.stdout.flush() 
    p = subprocess.Popen(["ls"], stderr=subprocess.PIPE, stdout=subprocess.PIPE, stdin=subprocess.PIPE) 
    print "launched subprocess, trying to communicate..." 
    sys.stdout.flush() 
    p.communicate() 
    print "returning" 
    sys.stdout.flush() 
    return "%032d" % (0) 

    def getActionFactory(self): 
     f = protocol.ServerFactory() 
     f.protocol = MyProtocol 
     f.action = self.action 
     return f 

reactor.suggestThreadPoolSize(300) 
application = service.Application('Action', uid=0, gid=0) 
f = ActionService() 
serviceCollection = service.IServiceCollection(application) 
internet.TCPServer(31337,f.getActionFactory() 
        ).setServiceParent(serviceCollection) 

...这是一些客户端代码:

#!/usr/bin/python 
import time 
import threading 
import socket 

def connectAction(host): 
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) 
    s.connect((host, 31337)) 
    s.send("asdf\r\n") 
    resp = s.recv(32) 
    s.close() 
    return resp 

class sscceThread(threading.Thread): 
    def __init__(self, host): 
     self.host = host 
     threading.Thread.__init__(self) 
    def run(self): 
     connectAction(self.host) 

def main(): 
    threads = [] 
    for i in range(0, 1000): 
     for j in range(0,5): 
      t = sscceThread("localhost") 
      t.start() 
      threads.append(t) 
     for t in threads: 
      t.join() 
     print i 
     time.sleep(1) 
    # print i 

if __name__ == "__main__": 
    main() 

运行启动服务:

twistd -y sscce_twisted_service.py -l twistdLog; tail -f twistdLog 

并运行客户机通过运行:

​​

你应该看到客户端进行了几次迭代(我已经看到它多达10个)然后挂起。客户端代码中包含有1秒的睡眠,让你可以告诉每个迭代上挂起你会看到在扭曲日志是这样一个扭曲的日志条目之间的区别:

2009-12-22 11:18:47-0800 [MyProtocol,55,127.0.0.1] launching subprocess 
2009-12-22 11:18:47-0800 [MyProtocol,56,127.0.0.1] launching subprocess 
2009-12-22 11:18:47-0800 [MyProtocol,55,127.0.0.1] launched subprocess, trying to communicate... 
2009-12-22 11:18:47-0800 [MyProtocol,57,127.0.0.1] launching subprocess 
2009-12-22 11:18:47-0800 [MyProtocol,58,127.0.0.1] launching subprocess 
2009-12-22 11:18:47-0800 [MyProtocol,56,127.0.0.1] launched subprocess, trying to communicate... 
2009-12-22 11:18:47-0800 [MyProtocol,55,127.0.0.1] returning 
2009-12-22 11:18:47-0800 [MyProtocol,57,127.0.0.1] launching subprocess 
2009-12-22 11:18:47-0800 [MyProtocol,56,127.0.0.1] launching subprocess returning 
2009-12-22 11:18:47-0800 [MyProtocol,59,127.0.0.1] launching subprocess 
2009-12-22 11:18:47-0800 [MyProtocol,58,127.0.0.1] launched subprocess, trying to communicate... 
2009-12-22 11:18:47-0800 [MyProtocol,58,127.0.0.1] returning 
2009-12-22 11:18:47-0800 [MyProtocol,59,127.0.0.1] launched subprocess, trying to communicate... 
2009-12-22 11:18:47-0800 [MyProtocol,59,127.0.0.1] returning 

特别请注意MyProtocol,57。它说它即将尝试启动子进程,但它从不打印“启动的子进程,尝试进行通信”行。我认为它一定是挂在那里的。

+1

在这里一切似乎都很好。 PS。使用'reactor.spawnProcess'而不是'subprocess' –

+0

你想接受答案吗? –

回答

6

由于毫克在他的评论中说,不要使用子进程模块。在POSIX平台上,有必要(或多或少)处理SIGCHLD信号以处理退出的子进程。由于只能有一个SIGCHLD处理程序,因此多个库通常不会合作。 Twisted的子进程支持和子进程模块的支持冲突。要么使用Twisted的支持(见http://twistedmatrix.com/documents/current/core/howto/process.html)或通过传递installSignalHandlers=Falsereactor.run(我推荐前者,因为subprocess礼物阻塞接口不顺利集成到基于双绞线的应用程序)关闭Twisted的支持。

+0

Twisted还可以让您控制继承的FD,这有时非常方便。有了子进程,如果你想使用stdout/stderr,你必须继承*所有* FDs,如果其中一个是一个侦听套接字,那么如果父节点不干净地终止,那么该子节点将它无限期地打开。 但有一点需要注意:如果您打算在Windows上部署此应用程序,则需要Python for Windows扩展,这些默认情况下不可用。我发现这很难,而且必须重写使用subprocess + reactor.iterate,因为当时不能部署额外的软件包。 – DNS

+0

请注意,此问题已在Twisted的trunk中修复,http://twistedmatrix.com/trac/ticket/733,但尚未发布。它将在Twisted 10.1中发布。 但是,spawnProcess仍然比子进程模块更加灵活和健壮。例如,即使Twisted被设置为使用更高效的事件循环,这意味着您可能无法在具有大量活动连接的服务器中产生子进程,并且JP关于阻止东西的注释不会生成子进程,但子进程将调用“select”很好地整合到Twisted中仍然很好。 – Glyph

+0

Twisted 10.1已经发布。 http://labs.twistedmatrix.com/2010/07/twisted-1010-released.html – Glyph