My setup has a Node.js child spawned which creates a 2-way ZeroRPC pair of sessions with a Python object.
The python side is similar to this:
class MyClass:
def __init__(self, socketpath):
self.client = zerorpc.Client()
self.client.connect(socketpath)
def sendtoclient(self, msg):
self.client.receiveMessage(msg)
if __name__ == '__main__':
zpc = zerorpc.Server(MyClass(sys.argv[1]))
zpc.bind(sys.argv[1] + "_python")
zpc.run()
The Node.js child client can invoke methods on the Python server, but the client within that server cannot invoke on the Node.js child server without getting an exception:
Traceback (most recent call last):
File "/usr/lib64/python2.6/site-packages/gevent/queue.py", line 271, in _unlock getter.switch(getter)
File "/usr/lib64/python2.6/site-packages/gevent/hub.py", line 534, in switch assert getcurrent() is self.hub, "Can only use Waiter.switch method from the Hub greenlet"
AssertionError: Can only use Waiter.switch method from the Hub greenlet
<callback at 0x3055e90 args=()> failed with AssertionError
Does the client in the Python class need to be spawned as a gevent and then its receiveMessage
method invoked when needed? Or is there some other trick I'm overlooking?
After some experimenting, a workable solution came from some examples in Gevent's documentation. The solution I went with is to create a gevent Queue that is populated from the server side and output from a separate Greenlet in a loop. So in the file with my server, I added:
import gevent
from gevent.queue import Queue, Empty
messagesOut = Queue()
def clientWorker(address):
client = zerorpc.Client()
if (None != client):
client.connect(address)
while True:
try:
messages = messagesOut.get()
client.passMessages(messages) # a method on the Node.js-side ZeroRPC server
except: # Empty could be thrown here if you're interested
pass
finally:
gevent.sleep(0)
In MyClass
, its initialization changed to retain a reference to the queue as self.outbox
(indeed, I could have used global each time I access it). When an async message needs to be sent, MyClass
calls self.outbox.put(messages)
.
Then down below when the ZeroRPC instances are created, I spawned each:
if __name__ == '__main__':
ge_client = gevent.spawn(clientWorker, sys.argv[1] + "_node2python")
zpc = zerorpc.Server(messagesOut)
zpc.bind(sys.argv[1] + "_python2node")
ge_server = gevent.spawn(zpc.run)
gevent.joinall([ge_client, ge_server]) # up and running here.
Doing the above resolved my problems as a proof-of-concept.