サービスを使用することをお勧めします。
サービスは、開始および停止されるTwistedアプリ内の機能の一部であり、コードの他の部分が対話するための優れた抽象化です。たとえば、この場合、SayStuffToServerService(ひどい名前ですが、その仕事について詳しく知らなくても、ここでできる最善の方法でした:))があり、次のようなものが公開されている可能性があります。
class SayStuffToServerService:
def __init__(self, host, port):
# this is the host and port to connect to
def sendToServer(self, whatToSend):
# send some line to the remote server
def startService(self):
# call me before using the service. starts outgoing connection efforts.
def stopService(self):
# clean reactor shutdowns should call this method. stops outgoing
# connection efforts.
(これで必要なインターフェイスはすべてこれで十分かもしれませんが、どこに追加できるかはかなり明確になっているはずです。)
ここでのメソッドstartService()
とstopService()
メソッドは、Twistedのサービスが公開しているものです。そして便利なことに、TCPクライアントのように機能し、すべてのリアクターの処理を行う、事前に作成されたツイストサービスがあります。これtwisted.application.internet.TCPClient
は、実際の接続試行の処理を処理するProtocolFactoryとともに、リモートホストとポートの引数を取ります。
これがSayStuffToServerServiceで、次のサブクラスとして実装されていTCPClient
ます。
from twisted.application import internet
class SayStuffToServerService(internet.TCPClient):
factoryclass = SayStuffToServerProtocolFactory
def __init__(self, host, port):
self.factory = self.factoryclass()
internet.TCPClient.__init__(self, host, port, self.factory)
def sendToServer(self, whatToSend):
# we'll do stuff here
(SayStuffToServerProtocolFactoryについては、以下を参照してください。)
このサービスアーキテクチャの使用は、多くの点で便利です。サービスを1つのコンテナーにグループ化すると、アプリのさまざまな部分をアクティブにしたいときに、サービスがすべて停止して1つとして開始されます。アプリの他の部分を個別のサービスとして実装することは理にかなっている場合があります。サービスを子サービスとして設定できます-アプリを初期化、デーモン化、およびシャットダウンする方法を知るために検索application
する魔法の名前。twistd
実際にはそうです、今それを行うためにいくつかのコードを追加しましょう。
from twisted.application import service
...
application = service.Application('say-stuff')
sttss = SayStuffToServerService('localhost', 65432)
sttss.setServiceParent(service.IServiceCollection(application))
それで全部です。このモジュールをtwistd
(つまり、デバッグのためにtwistd -noy saystuff.py
)で実行すると、適切なリアクターの下で開始され、SayStuffToServerServiceが開始されます。これにより、サービスの属性application
を使用するlocalhost:65432への接続が開始されます。factory
接続とプロトコルを設定します。reactor.run()
もう自分で原子炉に電話したり、物を取り付けたりする必要はありません。
したがって、SayStuffToServerProtocolFactoryはまだ実装していません。クライアントが接続を失った場合は再接続することをお勧めします(sendToServer
通常、の呼び出し元は接続が機能していると見なすことができます)ので、このプロトコルファクトリをに追加しReconnectingClientFactory
ます。
from twisted.internet import protocol
class SayStuffToServerProtocolFactory(protocol.ReconnectingClientFactory):
_my_live_proto = None
protocol = SayStuffToServerProtocol
これは非常に優れた最小限の定義であり、指定したホストとポートへの発信TCP接続を試行し続け、毎回SayStuffToServerProtocolをインスタンス化します。接続に失敗した場合、このクラスは、ネットワークが破壊されないように、適切に動作する指数バックオフを実行します(最大待機時間を設定できます)。指数バックオフが期待どおりに機能し続けるように、_my_live_proto
このファクトリのメソッドを割り当てて呼び出すのはプロトコルの責任です。resetDelay()
そして、これがそのプロトコルです。
class SayStuffToServerProtocol(basic.LineReceiver):
def connectionMade(self):
# if there are things you need to do on connecting to ensure the
# connection is "all right" (maybe authenticate?) then do that
# before calling:
self.factory.resetDelay()
self.factory._my_live_proto = self
def connectionLost(self, reason):
self.factory._my_live_proto = None
del self.factory
def sayStuff(self, stuff):
self.sendLine(stuff)
def lineReceived(self, line):
# do whatever you want to do with incoming lines. often it makes sense
# to have a queue of Deferreds on a protocol instance like this, and
# each incoming response gets sent to the next queued Deferred (which
# may have been pushed on the queue after sending some outgoing
# message in sayStuff(), or whatever).
pass
これは上に実装されtwisted.protocols.basic.LineReceiver
ますが、プロトコルが行指向でない場合は、他の種類のプロトコルでも同様に機能します。
残っているのは、サービスを適切なプロトコルインスタンスに接続することだけです。これが、ファクトリが_my_live_proto
属性を保持する理由です。この属性は、接続が正常に確立されたときに設定され、接続が失われたときにクリア(Noneに設定)される必要があります。これがの新しい実装ですSayStuffToServerService.sendToServer
:
class NotConnectedError(Exception):
pass
class SayStuffToServerService(internet.TCPClient):
...
def sendToServer(self, whatToSend):
if self.factory._my_live_proto is None:
# define here whatever behavior is appropriate when there is no
# current connection (in case the client can't connect or
# reconnect)
raise NotConnectedError
self.factory._my_live_proto.sayStuff(whatToSend)
そして今、それをすべて1つの場所にまとめます。
from twisted.application import internet, service
from twisted.internet import protocol
from twisted.protocols import basic
class SayStuffToServerProtocol(basic.LineReceiver):
def connectionMade(self):
# if there are things you need to do on connecting to ensure the
# connection is "all right" (maybe authenticate?) then do that
# before calling:
self.factory.resetDelay()
self.factory._my_live_proto = self
def connectionLost(self, reason):
self.factory._my_live_proto = None
del self.factory
def sayStuff(self, stuff):
self.sendLine(stuff)
def lineReceived(self, line):
# do whatever you want to do with incoming lines. often it makes sense
# to have a queue of Deferreds on a protocol instance like this, and
# each incoming response gets sent to the next queued Deferred (which
# may have been pushed on the queue after sending some outgoing
# message in sayStuff(), or whatever).
pass
class SayStuffToServerProtocolFactory(protocol.ReconnectingClientFactory):
_my_live_proto = None
protocol = SayStuffToServerProtocol
class NotConnectedError(Exception):
pass
class SayStuffToServerService(internet.TCPClient):
factoryclass = SayStuffToServerProtocolFactory
def __init__(self, host, port):
self.factory = self.factoryclass()
internet.TCPClient.__init__(self, host, port, self.factory)
def sendToServer(self, whatToSend):
if self.factory._my_live_proto is None:
# define here whatever behavior is appropriate when there is no
# current connection (in case the client can't connect or
# reconnect)
raise NotConnectedError
self.factory._my_live_proto.sayStuff(whatToSend)
application = service.Application('say-stuff')
sttss = SayStuffToServerService('localhost', 65432)
sttss.setServiceParent(service.IServiceCollection(application))
うまくいけば、それは開始するための十分なフレームワークを提供します。クライアントの切断を希望どおりに処理したり、サーバーからの異常な応答を処理したり、さまざまな種類のタイムアウトを処理したり、保留中の要求をキャンセルしたり、複数のプールされた接続を許可したりするために、多くの配管が必要になる場合があります。などですが、これは役立つはずです。