dcl-rpc
The Rust implementation of Decentraland RPC. At Decentraland, we have our own implementation of RPC for communications between the different services.
Currently, there are other implementations:
Requirements
- Install Just
Install Just for commands
Examples
Run the integration example
RPC Client in Rust and RPC Server in Rust running Websocket transport example, Memory Transport example and example using different types of transports
just run-integration
Run the integration example with an specific transport
RPC Client in Rust and RPC Server in Rust running the example passed to the command
just run-integration {ws|memory|dyn}
Run the multi language integration example
RPC Client in Typescript and RPC Server in Rust using WebSockets
just run-multilang
You can find the code for these examples in the examples/ directory.
Usage
Import
[]
= "*"
[]
= "*"
= "*" # As a build depency as well because we need the codegen module for the code-generation of the defined RPC Service in the .proto
Protobuf
Create a file app.proto to define the messages that will be used, for example:
syntax = "proto3";
package decentraland.echo;
message Text {
string say_something = 1;
}
service EchoService {
rpc Hello(Text) returns (Text) {}
}
Then, define a build.rs file to build the types of the message:
use Result;
The build.rs script runs every time that your .proto changes. The script will generate a file in the OUT_DIR, named as the package field in the .proto file (if it's not declared, the name will be '_.rs'). This file will include:
- All your declared messages in the
.protoas Rust structs. *1 - (
#[cfg(feature = "server")]) A trait, named{YOUR_RPC_SERVICE_NAME}Server: Send + Sync + 'static, with the methods defined in your service for the server side. So you should use this trait to build an implementation with the business logic. *2 - (
#[cfg(feature = "client")]) A trait, named{YOUR_RPC_SERVICE_NAME}ClientDefinition<T: Transport + 'static>: ServiceClient<T> + Send + Sync + 'static, and an implementation of it for the client side, named{YOUR_RPC_SERVICE_NAME}Client. You could use this auto-generated implementation when using theRpcClientpassing the implementation (struct with the trait implemented) as a generic in theload_modulefunction, which it'll be in charge of requesting the procedures of your service. But you could also have your own implementation of the{YOUR_RPC_SERVICE_NAME}ClientDefinitiontrait, as long as the implementations meets with trait's andRpcClientrequirements . *3 - (
#[cfg(feature = "server")]) A struct in charge of registering your declared service when aRpcServerPortis created. You should use this struct and its registering function inside theRpcServerport creation handler. *4
To import them you must add:
include!;
This statement should be added to the src/lib.rs in order to make the auto-generated code part of your crate, otherwise it will treat every include as different types.
Server Side
use ;
use crate::;
// Define the IP and Port where the WebSocket Server will run
let ws_server = new;
// Start listening on that IP:PORT
let mut connection_listener = ws_server.listen.await.unwrap;
// Add here any data that the server needs to solve the messages, for example db.
let ctx = MyExampleContext ;
let mut server = create;
server.set_handler;
// The WebSocket Server listens for incoming connections, when a connection is established, it creates a new WebSocketTransport with that connection and attaches it to the server event sender. The loop continues to listen for incoming connections and attach transports until it is stopped.
// and keep waiting for new ones
let server_events_sender = server.get_server_events_sender;
spawn;
server.run.await;
Implement the trait for your service
use crate::;
;
Client Side
Initiate a WebSocket Client Connection and send a Hello World message to the echo server.
use crate:: // (*3)
use ;
use Text;
let client_connection = connect
.await
.unwrap;
let client_transport = new;
let mut client = new.await.unwrap;
let port = client.create_port.await.unwrap;
let module = port..await.unwrap;
let response = module.hello.await;