6

Pyro を使用してスレーブ マシンを制御しようとしています。必要な python ファイルを rsync し、Pyro サーバーを起動し、リモート コントロールでいくつかのアクションを実行してから、Pyro サーバーにシャットダウンするように指示します。

Prio Daemon を正常にシャットダウンできません。呼び出しでハングするか、Daemon.close()その行をコメントアウトすると、ソケットを正しくシャットダウンせずに終了しsocket.error: [Errno 98] Address already in use、サーバーを再起動するのが早すぎます。

SO_REUSEADDR が適切な修正であるとは考えていません。ソケットが正しくシャットダウンされていないと、ソケットが TIME_WAIT 状態のままになり、一部のクライアントで問題が発生する可能性があるからです。より良い解決策は、Pyro Daemon にソケットを適切に閉じるよう説得することだと思います。

デーモン自体から Daemon.shutdown() を呼び出すのは不適切ですか?

サーバーを起動し、クライアントが接続されていない状態で CTRL-C を押しても、問題はありません (Address already in useエラーは発生しません)。これにより、ほとんどの場合、クリーンなシャットダウンが可能に見えます (それ以外の場合は正常なクライアントとサーバーを想定しています)。

例:server.py

import Pyro4

class TestAPI:
    def __init__(self, daemon):
        self.daemon = daemon
    def hello(self, msg):
        print 'client said {}'.format(msg)
        return 'hola'
    def shutdown(self):
        print 'shutting down...'
        self.daemon.shutdown()

if __name__ == '__main__':
    daemon = Pyro4.Daemon(port=9999)
    tapi = TestAPI(daemon)
    uri = daemon.register(tapi, objectId='TestAPI')
    daemon.requestLoop()
    print 'exited requestLoop'
    daemon.close() # this hangs
    print 'daemon closed'

例:client.py

import Pyro4

if __name__ == '__main__':
        uri = 'PYRO:TestAPI@localhost:9999'
        remote = Pyro4.Proxy(uri)
        response = remote.hello('hello')
        print 'server said {}'.format(response)
        try:
            remote.shutdown()
        except Pyro4.errors.ConnectionClosedError:
            pass
        print 'client exiting'
4

2 に答える 2

6

これは、timeout や loopCondition を使用せずにshutdown()、デーモンのshutdown. http://pythonhosted.org/Pyro4/servercode.html#cleaning-upによると:

別の可能性は、実行中の bdaemon オブジェクトで Pyro4.core.Daemon.shutdown() を呼び出すことです。これはまた、リクエスト ループから抜け出し、コード自体をきれいにクリーンアップできるようにし、他の要件なしでスレッド化されたサーバー タイプでも機能します。

以下は、Windows 上の Python3.4.2 で動作します。ここでは@Pyro4.onewayデコレータ forshutdownは必要ありませんが、場合によっては必要です。

server.py

import Pyro4
# using Python3.4.2

@Pyro4.expose
class TestAPI:
    def __init__(self, daemon):
        self.daemon = daemon
    def hello(self, msg):
        print('client said {}'.format(msg))
        return 'hola'
    @Pyro4.oneway   # in case call returns much later than daemon.shutdown
    def shutdown(self):
        print('shutting down...')
        self.daemon.shutdown()

if __name__ == '__main__':
    daemon = Pyro4.Daemon(port=9999)
    tapi = TestAPI(daemon)
    uri = daemon.register(tapi, objectId='TestAPI')
    daemon.requestLoop()
    print('exited requestLoop')
    daemon.close()
    print('daemon closed')

client.py

import Pyro4
# using Python3.4.2

if __name__ == '__main__':
    uri = 'PYRO:TestAPI@localhost:9999'
    remote = Pyro4.Proxy(uri)
    response = remote.hello('hello')
    print('server said {}'.format(response))
    remote.shutdown()
    remote._pyroRelease()
    print('client exiting')
于 2016-12-27T23:52:23.740 に答える
0

私は解決策に近いと思います:loopConditionパラメータ torequestloop()と config valueの使用の組み合わせCOMMTIMEOUT

server.py

import Pyro4
Pyro4.config.COMMTIMEOUT = 1.0 # without this daemon.close() hangs

class TestAPI:
    def __init__(self, daemon):
        self.daemon = daemon
        self.running = True
    def hello(self, msg):
        print 'client said {}'.format(msg)
        return 'hola'
    def shutdown(self):
        print 'shutting down...'
        self.running = False

if __name__ == '__main__':
    daemon = Pyro4.Daemon(port=9999)
    tapi = TestAPI(daemon)
    uri = daemon.register(tapi, objectId='TestAPI')
    def checkshutdown():
        return tapi.running
    daemon.requestLoop(loopCondition=checkshutdown) # permits self-shutdown
    print 'exited requestLoop'
    daemon.close()
    print 'daemon closed'

残念ながら、TIME_WAIT 状態のままソケットが残る状況が 1 つあります。クライアントがサーバーの後にソケットを閉じると、次にサーバーを起動しようとすると同じAddress already in useエラーが返されます。

これを回避する唯一の方法は、サーバーの COMMTIMEOUT を長くして (または を呼び出す前に数秒間スリープさせてdaemon.close())、クライアントが常に_pyroRelease()シャットダウン呼び出しの直後に呼び出すようにすることです。

client.py

import Pyro4

if __name__ == '__main__':
        uri = 'PYRO:TestAPI@localhost:9999'
        remote = Pyro4.Proxy(uri)
        response = remote.hello('hello')
        print 'server said {}'.format(response)
        remote.shutdown()
        remote._pyroRelease()
        print 'client exiting'

これで十分だと思いますが、スケジューリングの不公平とネットワークの遅延を考えると、競合状態が潜んでいるのはまだ残念です.

于 2014-06-27T23:04:53.347 に答える