如何通过客户端请求干净地退出Pyro Daemon?

Eri*_*nen 6 python sockets pyro

我正在尝试使用Pyro来控制一台奴隶机器.我rsync必要的python文件,启动Pyro服务器,通过远程控制执行一些操作,然后我想告诉Pyro服务器关闭.

我无法让Pryo 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'
Run Code Online (Sandbox Code Playgroud)

例: 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'
Run Code Online (Sandbox Code Playgroud)

小智 6

我认为可以通过shutdown()调用守护程序的而无需使用timeout或loopCondition来完成此操作shutdown。根据http://pythonhosted.org/Pyro4/servercode.html#cleaning-up

另一种可能性是在正在运行的bdaemon对象上调用Pyro4.core.Daemon.shutdown()。这也将打破请求循环,并允许您的代码在执行之后进行整齐的清理,并且还可以在线程服务器类型上工作,而无需任何其他要求。

以下内容适用于Windows上的Python3.4.2。该@Pyro4.oneway用于装饰shutdown在这里不需要,但在某些情况下。

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')
Run Code Online (Sandbox Code Playgroud)

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')
Run Code Online (Sandbox Code Playgroud)


Eri*_*nen 0

loopCondition我认为我已经接近解决方案:使用参数 torequestloop()和配置 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'
Run Code Online (Sandbox Code Playgroud)

不幸的是,在一种情况下,它仍然会将套接字留在 TIME_WAIT 状态。如果客户端在服务器之后关闭其套接字,则下次尝试启动服务器时将返回相同的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'
Run Code Online (Sandbox Code Playgroud)

我认为这已经足够好了,但考虑到调度的不公平性和网络延迟,潜伏着的竞争条件仍然令人失望。