Features

joint is able to replace all other methods of inter-process communication, even between large group of servers. It is designed to do this.

Full support for MessagePackRPC protocol

  • You can connect with joint-client to other servers, which implement MessagePackRPC.
  • You can as well connect by other client libraries which implement MessagePackRPC to joint-server.

Various transports

joint api carefully designed, so it allows to use any transport layer (tcp, udp, zeromq, etc.)

Streaming

We allow bidirectional streaming between server and client during call. This is done via yield statement in server side and .send() method of proxy generator on client. This communication can be either synchronous or asyncrhonous, depending on request flags.

Imagine, you want to transfer 1gb file. Traditional RPC_ will prepare result on server in memory, send to client, client prepares result while receiving and only after that return as a result of remote function call. This way 1gb memory (min) used on both server and client.

joint can handle this much more effeciently. Server side function can look like this:

def read1gb(fn):
    with open(fn) as fp:
        while True:
            data = fp.read(128 * 1024)  # read by 128kb
            if not data:
                break
            yield data

So, basically, we read 128kb chunk and yield it.

Client just uses call as iterator and iterates thru results:

with open('/some/path', 'wb') as fp:
    for chunk in remote.call('/path/to/1gb/file/on/remote/machine'):
        fp.write(chunk)

Thats all! We didn’t used 1gb memory in both client and server and transfered that huge file.

Big packets splitting

For example, you want to send 128mb of data to client which connected within 8mbit network to server:

--> call func
<-- ok, calling
(sending 128mb of data in 128 seconds)

Anything good? Yes and no. While transferring 128mb server is not able to call any other meths in parallel in that connection. So, if you call something during receiving 128mb – results will be sent only after first result:

--> (thread 1) call func ("get 128mb!")
<-- ok, calling #1
--> (thread 2) call func ("get 1 byte!")
(sending 128mb of data in 128 seconds)
<-- ok, calling #2
(receiving 1 byte from another call)

joint handles this automatically. It splits each packet send to client (and wise versa!) in chunks. So, in joint this looks like this:

--> (thread 1) call func ("get 128mb!")
<-- ok, calling #1
--> (thread 2) call func ("get 1 byte!")
(sending chunk 1 of #1 result)
(sending chunk 1 of #2 result)
(sending chunk 2 of #1 result)
...
(sending chunk N of #1 result)

Furthermore, joint splits request packets from client as well. So, if you have a lot of data in func arguments – everything should be good.

Integration with existing event loops

You can use joint library with existing event loops, where is no need to create another one dediceted to RPC. For example, to use zeromq:

# server
sock = zmq.Socket(zmq.REP)
rpc_dispatcher = joint.server.Dispatcher()
while True:
   msg = sock.recv()
   response = rpc_dispatcher.dispatch(msg)
   sock.send(response)

# client
sock = zmq.Socket(zmq.REQ)
sock.connect('tcp://server/addr')
rpc = joint.Client(None)

job = rpc.call_async('some_func')
sock.send(job.query)

response = sock.recv()
job.dispatch(response)

result = job.join()