Inter-module RPC usage¶
There are two methods implemented for inter-module RPC: moduleMethod
and moduleMethodResponse
. When using moduleMethod
there is one huge difference to all other RPC methods: The call is asynchronous. This means, you won't get a response directly to moduleMethod
. Instead when the request is finished, moduleMethodResponse
is called as a request on the originating client.
Warning
There is never a response received to moduleMethod
. Instead moduleMethodResponse
is called on the originating module.
The reason for this implementation is, that a request might take a long time to process. This hanging request then blocks a lot of processing threads on multiple processes or services. This again might cause Sensaru Cloud to hang.
Routing¶
Routing packets within Sensaru Cloud is not done by IP address, but by module and module client ID. moduleMethod
and moduleMethodResponse
are used as a container for the actual RPC request. So these methods are sort of our equivalent of an IP header. For module to module communication, routing requires only the module ID. Clients connected to modules (i. e. module clients) additionally require the module client ID for routing. So we have four possible routing combinations:
Direction | Description |
---|---|
Module to module | When moduleMethod is called from a module on another module, we only need the source module ID and the destination module ID for routing. |
Module to module client | When moduleMethod is called from a module on a client of another module, we need the source module ID, the destination module ID and the destination module client ID for routing. |
Module client to module | When moduleMethod is called from a module client on a module, we need the source module ID, the source module client ID and the destination module ID for routing. |
Module client to module client | When moduleMethod is called from a module client on another module client, we need the source module ID, the source module client ID, the destination module ID and the destination module client ID for routing. |
One important thing to note is that a module client is not allowed to put the source module client ID in moduleMethod
if it is in an unsecure location or cannot be trusted. In this case this must be done in the module the client is connected to. Here the module client ID can be verified and placed into moduleMethod
.
moduleMethod
¶
Calling module method¶
Whenever you want to call a RPC method on another module or module client, you need to execute moduleMethod
. moduleMethod
has the following signature:
void moduleMethod(
String destinationModuleId,
String destinationModuleClientId = "",
String senderModuleId,
String senderModuleClientId = "",
Struct userId = [],
Struct modulePrincipalId = {},
Struct homeClientUsers = {},
Struct homeClients = {},
String methodName,
Array parameters = [],
String ticketId,
Integer qos
)
The signature (obviously) is the same on the sender and receiver side (except in homegear-cloudconnect
which has a simplified signature). The sender does not need to fill all parameters though, as most of them are filled in transit by other services.
Parameter | Set by | Optional | Type | Description |
---|---|---|---|---|
destinationModuleId |
Sender | no | String |
The ID of the module to call the RPC method in (e. g. c1-device-management ) |
destinationModuleClientId |
Sender | yes | String |
The ID of the module client to call the RPC method in (e. g. d1faa8d0-2db4-11ea-af75-674069e60b74_1000.1.1_1 ). A module client as a service connected to a module, e. g. edge clients connected to c1-proxy . |
senderModuleId |
c1-core | no | String |
The ID of the sending module. This parameter is filled (or overridden) in c1-core with the ID of the sending module. |
senderModuleClientId |
Module | yes | String |
The ID of the sending module client. Needs to be set to a verified value by the module. This can't be checked by c1-core . The value is only required for requests from module clients. |
userId |
Sender | yes | Struct |
Filled with the ID of the sending user. For requests coming from UIs to c1-core this is filled with the logged in user. This parameter can be set by any sender. Just make sure the information is verified, as the validity cannot be checked by c1-core . |
modulePrincipalId |
c1-proxy / c1-core | yes | Struct |
Filled with the edge client's principal ID for requests coming from edge clients and with and with the module's principal ID for requests coming from modules. The latter is relevant, because modules can be associated to a principal and access to other principals must be denied in this case. |
homeClientUsers |
c1-core | yes | Struct |
Filled with the users associated to the edge client specified with homeClientId . |
homeClients |
c1-core | yes | Struct |
Filled with the edge client IDs associated to the user specified with userId . |
methodName |
Sender | no | String |
The RPC method to call. |
parameters |
Sender | yes | Array |
The parameters to pass to the RPC method. |
ticketId |
Sender | no | String |
This parameter is like a password. Fill this parameter with fully random string of appropriate length. Every request requires a unique ticket ID. When receiving moduleMethodResponse , the ticked ID must be matched. |
qos |
Sender | no | Integer |
0 to disable QoS . 1 causes the RPC call to be cached until the destination acknowledges the request. |
Example¶
To for example call getVersion()
on a edge client a request might look like this:
{
"id": 12,
"method": "moduleMethod",
"params": [
"c1-proxy",
"d1faa8d0-2db4-11ea-af75-674069e60b74_1000.1.1_1",
"",
"",
{},
{},
{},
{},
"getVersion",
[],
"abcd",
0
]
}
To call this method using c1-module-proxy
, execute:
$ curl -X POST -H 'Content-Type: application/json' -d '{"id":12,"method":"moduleMethod","params":["c1-proxy", "d1faa8d0-2db4-11ea-af75-674069e60b74_1000.1.1_1", "", "", [], "", {}, {}, "getVersion", [], "abcd", 0]}' http://localhost:8080
The response will be:
{"id":12,"jsonrpc":"2.0","result":null}
After this, moduleMethodResponse
is called (as a request!).
Warning
When you're using c1-module-proxy
, the response is returned directly in moduleMethod
and moduleMethodResponse
is not called.
Receiving moduleMethod
¶
When moduleMethod
is called on a module (not a module client), c1-core
invokes the actual method within the moduleMethod
call. This means, you will not receive a call to moduleMethod
. So when another service is calling the following on your module:
{
"id": 12,
"method": "moduleMethod",
"params": [
"c1-proxy",
"d1faa8d0-2db4-11ea-af75-674069e60b74_1000.1.1_1",
"",
"",
{},
{},
{},
{},
"getVersion",
[],
"abcd",
0
]
}
Then you will receive the following in your module:
{
"id": 12,
"method": "getVersion",
"params": [
<verified metadata>
]
}
moduleMethod
and module clients¶
You are only required to implement moduleMethod
within your module, when the module has module clients. In this case c1-core
does not invoke the wrapped method but passes moduleMethod
to your module. In your module you can then identify the endpoint and invoke the wrapped RPC method directly as the routing information is not required anymore.
Note
When receiving moduleMethod
you will notice that all or most of the parameters have been filled.
moduleMethodResponse
¶
moduleMethodResponse
is sent after processing of a call to moduleMethod
has finished. It has the following signature:
void moduleMethodResponse(
String destinationModuleId,
String destinationModuleClientId = "",
String senderModuleId,
String senderModuleClientId = "",
Struct userId = {},
Struct modulePrincipalId = {},
Struct homeClientUsers = {},
Struct homeClients = {},
Mixed response,
String ticketId,
Integer qos
)
As you can see, most parameters are the same as for moduleMethod
. The only difference is that methodName
and parameters
are replaced by response
. The latter contains the result of the call to the RPC method specified by methodName
in moduleMethod
. The ticked ID must be set to the one from the moduleMethod
call. Apart from that the same rules as for moduleMethod
apply.
Example¶
An example request looks like this:
{
"id": 6,
"jsonrpc": "2.0",
"method": "moduleMethodResponse",
"params": [
"c1-proxy",
"d1faa8d0-2db4-11ea-af75-674069e60b74_1000.1.1_1",
"c1-device-management",
"",
{},
{},
{},
{},
"Homegear 0.8.0-3348",
""
]
}
The response looks like this:
{"id":6,"jsonrpc":"2.0","result":null}