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