Strange issues with multiprocessing with zeroRPC

982 views Asked by At

So, I'm playing around with ZeroRPC and Tornado for a pet project and I've run into some issues using ZeroRPC in conjunction with python's multiprocessing library. Specifically, I'm creating and running new ZeroRPC servers programmatically, but typically when run, the zerorpc server is blocking, so my thought was to throw it into another process like so:

 server = zerorpc.Server(FuncWrapper())
 server.bind(server_address)
 process = multiprocessing.Process(target=server.run)
 process.start()

However, when I do this, calling the RPC server just hangs, which is typical behavior when an endpoint hasn't been correctly instantiated. However, if I just let the server running block and call it like so:

 serhouldver = zerorpc.Server(FuncWrapper())
 server.bind(server_address)
 server.run()

Everything works fine. My understanding was that these two implementations should be equivalent, but somehow they are not.

Any ideas?

1

There are 1 answers

0
bombela On

zerorpc uses gevent for cooperative asynchronous IO. You might want to look into how tornado, multiprocessing and gevent are playing all together.

For what I can tell in:

server = zerorpc.Server(FuncWrapper())
server.bind(server_address)
process = multiprocessing.Process(target=server.run)
process.start()

Line 1 and 2 are creating and binding a port on the current process. But at line 3 and 4, what I can guess happening is:

  • fork is called, all threads in the process are lost in the fork
  • any zeromq socket & context are dead now (no more thread). Good news is, the context can be destroyed, and a new one created (see http://lists.zeromq.org/pipermail/zeromq-dev/2014-January/024536.html).
  • Now you have a port open on your local process, with an active zeromq socket, but nobody is reading from this socket (hence no reaction when you talk to it).
  • On the other side, in the multiprocess's process, zerorpc is running, calling recv on the relics of the original zmq socket. Nothing will never come, zeromq is dead already.

Without testing, I can only guess that running zerorpc completely in the new process should work:

def ZeroRPC_Service():
  server = zerorpc.Server(FuncWrapper())
  server.bind(server_address)
  server.run()

process = multiprocessing.Process(target=ZeroRPC_Service)
process.start()

Then via some Manager object or other Shared memory services offered by multiprocess, you could make the zerorpc server in the new process access and share data with your local process.

On a side note, if what you are trying to do, is call server.run() without blocking, if you were using only gevent, I would tell you to simply run it in its own coroutine:

server = zerorpc.Server(FuncWrapper())
server.bind(server_address)
server_coro = gevent.spawn(server.run)

Maybe you can call server.run from a tornado coroutine/asyunc function directly. Maybe there is a way to integrate gevent and tornado (like this link suggest: https://bmmh.wordpress.com/2012/10/07/tornado-and-gevent/), I don't know enough to help you at this point.