Remote Calls

To perform remote procedure calls you just need to connect two peers, and then, on your local peer instance, call a registered function with the right parameters. You will then receive the return value of the remotely executed function.

# server.py
import string

import pseud
from pseud.utils import register_rpc


server = pseud.Server('remote')
server.bind('tcp://127.0.0.1:5555')

# register locally for this server only
server.register_rpc(string.lower)
# register globally for all rpc instances
register_rpc(string.upper)

await server.start()
# client.py
import pseud


client = pseud.Client('remote')
client.connect('tcp://127.0.0.1:5555')

res1 = await client.lower('ABC')
res2 = await client.upper('def')

assert res1 == 'abc'
assert res2 == 'DEF'

Registration

Registration is a necessary step to control what callable you want to expose for remote peers.

Global

The register_rpc decorator from pseud.utils module must be used to register a callable for all workers of the current process.

from pseud.utils import regsiter_rpc


@register_rpc
def call_me():
     return 'Done'

Local

An RPC instance exposes its own register_rpc function, which is used to register a callable only for that same RPC instance.

def call_me():
    return 'Done'

server.register_rpc(call_me)

You can also instantiate a registry and give it to pseud.utils.register_rpc, and pass it as an init parameter in the RPC. It is more convenient to use register_rpc as a decorator

import pseud
from pseud.utils import register_rpc, create_local_registry

registry = create_local_registry('worker')

@register_rpc(registry=registry)
def call_me():
    return 'Done'

server = pseud.Server('worker', registry=registry)

Name it !

You can also decide to provide your own name (dotted name) to the callable

from pseud.utils import regsiter_rpc


@register_rpc('this.is.a.name')
def call_me():
     return 'Done'
client.this.is.a.name().get() == 'Done'

Server wants to make the client do work

In order to let the server send jobs to its connected clients, the caller should know the identity of the specified client beforehand. By default all clients are anonymous for the server. This is why it is necessary to rely on your own security_plugin to perform the authentication.

The most simple authentication that you can use is plain for the client, by passing user_id and password arguments to the constructor. Then on the server side trusted_peer will just trust that given user_id will identify the peer, and ignore the password.

Given a client whose identity is 'client1', with a registered function named addition, the following statement may be used to send work from the server to the client:

# server.py
server = Server('service', security_plugin='trusted_peer')
server.bind('tcp://127.0.0.1:5555')
await server.start()
# client.py
client = Client('service',
                 security_plugin='plain',
                 user_id='client1',
                 password='')

client.connect('tcp://127.0.0.1:5555')

@client.register_rpc
def addition(a, b):
    return a + b

await client.hello('Me')  # perform a first call to register itself

Note

The client needs to perform at least one call to the server to register itself. Otherwise the server won’t know a client is connected to it. On real condition the heartbeat backend will take care of it. So you do not have to worry about it.

# server.py

result = await server.send_to('client1').addition(2, 4)
assert result == 6

Note

the client1 string is the user_id provided by the client.