All VRPC agents and clients are MQTT clients to a single logical broker. They use the MQTT 3.11 standard to realize all RPC functionality.
Instead of being message oriented, VRPC uses a protocol on top of MQTT fixing the topic as well as the payload structure.
General topic pattern:
<domain>/<agent>/<class>/<instance>/<function>
NOTE
- if
<function>
refers to a static method<instance>
must have value__static__
- if
<function>
refers to a global method<class>
must have value__global__
and<instance>
must have value__static__
General RPC request payload pattern:
{
"c": "<context>",
"f": "<function>",
"a": ["<arguments>"],
"i": "<correlationId>",
"s": "<sender>",
"v": "<protocolVersion>"
}
General RPC response payload pattern:
{
"a": ["<arguments>"],
"r": "<return value>",
"e": "<error message>",
"i": "<correlationId>",
"v": "<protocolVersion>"
}
NOTE 1
- if
<function>
refers to a member function,<context>
must reflect the corresponding instance name- if
<function>
refers to a static function,<context>
must reflect the corresponding class name- if
<function>
refers to a global function,<context>
must have the value__global__
NOTE 2
If VRPC is used for language embedding use cases (i.e. not remotely), the last two properties
i
(correlationId) ands
(sender) are omitted from the call.NOTE 3
For all languages supporting function overloading,
<method>
must carry the full signature in form of:<methodName>[-<typename arg1>:[<typename arg2>:][...]]]where
typename
can be one of:
- null
- object
- array
- string
- boolean
- number
NOTE 4
In case of an successful RPC call the property
e
MUST NOT exist in the response message.
-
User configures
<domain>
and<agent>
-
VRPC generates an MQTT client ID like so:
va3<agentInstance>
where
<agentInstance>
reflects a 20 characters long hash generated out of domain and agent name. -
VRPC publishes the (retained) agent info message:
<domain>/<agent>/__agentInfo__ JSON PAYLOAD { "status": "online" "hostname": <hostname> "version": <userAgentVersion> }
-
VRPC iterates all registered classes and subscribes to their static functions using the following pattern:
<domain>/<agent>/<class>/__static__/<method>
-
VRPC then publishes a class info message for each registered class:
<domain>/<agent>/<class>/__classInfo__ JSON PAYLOAD { "className": <className>, "instances": [<instance1>, <instance2>, ...], "staticFunctions": [<function1>, <function2>, ...], "memberFunctions": [<function1>, <function2>, ...], "meta": { <function1>: { description, params, ret } <function2>: { description, params, ret } } }
NOTE
The
meta
property is optional and only available if meta information could be acquired from the adapted code (e.g. js-doc like comments) When available, not all functions may be listed, those without meta information are skipped.
-
After receiving an RPC message on topic:
<domain>/<agent>/<class>/__static__/__createIsolated__
VRPC creates and names a new instance, iterates all of its member functions and subscribes to each one using:
<domain>/<agent>/<class>/<instance>/<method>
-
After receiving an RPC message on topic
<domain>/<agent>/<class>/<instance>/<method>
VRPC executes the RPC call and then replies to the sender instance by publishing to the topic that was provided in the
<sender>
property.
Either by explicitly calling the end()
function or by MQTT last will
VRPC publishes the (retained) agent info message:
<domain>/<agent>/__agentInfo__
JSON PAYLOAD {
status: 'offline'
hostname: <hostname>
version: <userAgentVersion>
}
-
User (optionally) configures
<domain>
and<agent>
, those will be the defaults for later remote instances -
VRPC generates an MQTT client ID like so:
vc3<random><processInfo>
where
<random>
are 8 random characters and<processInfo>
is a 12 character long string composed of host specific properties (i.e. stays the same on the same host) -
VRPC then listens for available agents and classes by subscribing to
<domain>/<agent>/__agentInfo__
and
<domain>/<agent>/+/__classInfo__
-
Finally, VRPC listens to RPC responses by subscribing to
<domain>/<host>/<random>
which can be regarded as VRPC client ID. This information is packed into the
<sender>
property of the RPC payload.
-
VRPC publishes a single message per RPC request to
<domain>/<agent>/<class>/<instance>/<method>
where
<instance>
is replaced with__static__
in case of static method calls, and<class>
is replaced with__global__
in case of global method calls. -
In case an argument of the remotely called method is of function type (i.e. a callback), the corresponding data argument will be a string that reads:
__f__<methodName>-<index>-<event|invokeId>
Depending on the callback type (continuous or re-registered) either the event name or an invoke id is used as last identifier.
Either by explicitly calling the end()
function or by MQTT last will
VRPC publishes the (non-retained) client info message:
<domain>/<host>/<random>/__clientInfo__
JSON PAYLOAD {
status: 'offline'
}
With respect to the remote procedure calls the Adapter is a passive component. Besides registering the to-be-remotified functions, the user does not directly interact with this component. It however intercepts the regular RPC traffic in two distinct ways.
Those are the function to manage the lifetime of the remotely created objects:
__createShared__
__createIsolated__
__callAll__
__delete__
all of them use double underscores both as prefix and as postfix.
The adapter will intercept all remotified functions that return a promise. The original promise return value is changed to a string that reads:
__p__<method>-<invokeId>
At the time of promise resolution the adapter will send another RPC response
containing the actual return value and the above mentioned string as <id>
.