2009-12-22 134 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