RPC CommunicationLink
OverviewLink
RPC or Remote Procedure Call is a communication pattern between a client and a server where the client can access an object provided by the server as if it was a local object. In a sense, the client serves as a proxy of an object and the server makes the actual object available.
In Plant-Imager3 this communication protocol is implemented in the module plantimager.commons.RPC
with the main two
classes RPCClient
and RPCServer
as well as other auxiliary classes RPCSignal
, RPCProperty
and RPCSignalReceiver
.
RPCServer
: When used as a parent to a class, makes that class available as an RPC objectRPCClient
: Proxy of an RPC object which connects to theRPCServer
providing said objectRPCSignal
: Declare signals for RPC objects which when emitted server-side are also emitted client-side, calling any connected methodRPCProperty
: Declare python-style properties that are made available to the clientRPCSignalReceiver
: Internal signal receiver client-side in charge of receiving and copying signals from the server
Communication diagramLink
Class diagramLink
Class diagram for a typical implementation where we want to make available te class AbstractDevice
. To that end we
implement DeviceServer
which inherits from both AbstractDevice
and RPCServer
and in which we implement the various
methods defined in the abstract class; DeviceServer
will serve the implementation.
Client-side we simply declare a class DeviceClient
inheriting from both AbstractDevice
and RPCClient
and which must
be decorated with the classmethod RPCClient.register_interface()
. Implementation of the abstract methods of AbstractDevice
is automatically handled, creating proxy methods which will call the proper methods from DeviceServer
.
---
title: Animal example
---
classDiagram
note for AbstractDevice "Abstract class defining the interface which will be exposed via RPC"
RPCServer <|-- DeviceServer
AbstractDevice <|-- DeviceServer
AbstractDevice <|-- DeviceClient
RPCClient <|-- DeviceClient
RPCClient *-- RPCSignalReceiver
class RPCServer {
+zmq.Context context
+String url
+int port
+String name
+String registry_addr
+String peer_addr
-zmq.Socket _socket
+register_to_registry(type_name, name, registry_url)
+register_method_json(timeout)$
+register_method_buffer(timeout)$
+stop_server()
+serve_forever()
-_send_signal(signal_name, *args)
-_exec_json(method, params)
-_exec_buffer(method, params)
-_finalize()
}
class RPCClient {
+zmq.Context context
+String url
+zmq.Socket socket
+String own_address
+String peer_address
+String name
+execute(method_name, params)
+stop_server()
+register_interface(interface)$
-_finalizer()
-_method_proxy(func, self, *args, **kwargs)$
-_property_getter_proxy(property_name)
-_property_setter_proxy(value, property_name)
}
class RPCSignalReceiver {
+zmq.Context context
+String url
+dict[str, RPCSignals] signals
+zmq.Socket socket
+int port
+run()
+stop()
}
class AbstractDevice{
<<Abstract>>
+RPCSignal some_signal$
+RPCProperty some_property*
+some_method()*
}
class DeviceServer{
}
class DeviceClient{
}
Connection diagramLink
sequenceDiagram
participant signal_recv as RPCSignalReceiver
participant client as Device Client
participant server as Device Server
activate client
Note over server: The method serve_forever() must have been called
Note over client: in __init__ after <br/> the socket is connected
client->>+server: FIND_PEER_ADDRESS
Note right of server: Creates a socket server
server->>client: url
client<<-->>server: connects to temp server
deactivate server
Note over client,server: Getting peer address and <br/> closing connection and temporary server
client->>+server: GET_INVENTORY
server->>-client: methods, properties and signals inventory
opt RPCSignals declared
Note over signal_recv, client: Start Signal Receiver thread
client->>+signal_recv: __init__()
signal_recv-->>-client:
client->>+server: INIT_SIGNALS_HANDLING
Note right of server: Connect to signal socket and <br/> connect signals to _send_signal()
server->>-client: success
end
deactivate client
Method callLink
sequenceDiagram
participant other
participant client as Device Client
participant server as Device Server
Note over client, server: server.serve_forever() called <br/> and client connected
other->>+client: some_method()
Note over client: some_method is a proxy method
client->>+client: execute()
Alt is json_method
client->>+server: METHOD_CALL
server->>+server: _exec_json()
server->>server: some_method()
deactivate server
server->>-client: result
else is buffer_method
client->>+server: METHOD_CALL
server->>+server: _exec_buffer()
server->>server: some_method()
deactivate server
server->>-client: result
end
deactivate client
client-->>-other: result
Emitting a signalLink
sequenceDiagram
participant client_signal as Signal proxy
participant recv as RPCSignalReceiver
participant server as Device Server
participant server_signal as Original Signal
Note over recv, server: server.serve_forever() called <br/> and client connected
Note over server_signal: emit() called
activate server_signal
server_signal->>+server: _send_signal()
server->>+recv: EMIT_SIGNAL
Note over recv: select corresponding signal
alt is blocking
recv->>+client_signal: emit()
client_signal-->>-recv:
recv->>server: success
else
recv->>server: success
recv->>+client_signal: emit()
client_signal-->>-recv:
deactivate recv
end
server-->>-server_signal:
deactivate server_signal
Property getterLink
sequenceDiagram
participant other
participant client as Device Client
participant server as Device Server
Note over client, server: server.serve_forever() called <br/> and client connected
other->>+client: some_property getter()
Note over client: some_property getter is a proxy method <br/> RPCClient._property_getter_proxy() is called
client->>+server: PROPERTY_GET
server->>server: some_property getter()
server->>-client: result
client-->>-other: result
Property setterLink
sequenceDiagram
participant other
participant client as Device Client
participant server as Device Server
Note over client, server: server.serve_forever() called <br/> and client connected
other->>+client: some_property setter()
Note over client: some_property setter is a proxy method <br/> RPCClient._property_setter_proxy() is called
client->>+server: PROPERTY_SET
server->>server: some_property setter()
server->>-client: result
client-->>-other: result