15 个版本 (稳定)
2.3.5 | 2023 年 5 月 24 日 |
---|---|
2.3.4 | 2023 年 5 月 16 日 |
2.0.1 | 2023 年 4 月 28 日 |
1.0.0 | 2023 年 3 月 21 日 |
0.1.3 | 2023 年 2 月 28 日 |
在 WebSocket 类别中排名 57
每月下载量 82 次
180KB
3K SLoC
dcl-rpc
Decentraland RPC 的 Rust 实现。在 Decentraland 中,我们对不同服务之间的通信有自己实现的 RPC。
目前,还有其他实现
要求
- 安装 Just
仅安装 Just 以用于命令
cargo install just
示例
运行集成示例
Rust 中的 RPC 客户端和 Rust 中的 RPC 服务器运行 WebSocket 传输示例、内存传输示例以及使用不同类型传输的示例
只需运行-integration
使用特定传输运行集成示例
Rust 中的 RPC 客户端和 Rust 中的 RPC 服务器运行通过命令传递的示例
只需运行-integration{ws|memory|dyn}
运行多语言集成示例
使用 WebSockets 的 TypeScript RPC 客户端和 Rust RPC 服务器
只需运行-multilang
这些示例的代码可以在 examples/
目录中找到。
用法
导入
[dependencies]
dcl-rpc = "*"
[build-dependencies]
prost-build = "*"
dcl-rpc = "*" # 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
创建一个文件 app.proto
来定义将要使用的消息,例如
syntax = "proto3";
package decentraland.echo;
message Text {
string say_something = 1;
}
service EchoService {
rpc Hello(Text) returns (Text) {}
}
然后,定义一个 build.rs
文件来构建消息的类型
use std::io::Result;
fn main() -> Result<()> {
// Tell Cargo that if the given file changes, to rerun this build script.
println!("cargo:rerun-if-changed=src/echo.proto");
let mut conf = prost_build::Config::new();
conf.service_generator(Box::new(dcl_rpc::codegen::RPCServiceGenerator::new()));
conf.compile_protos(&["src/echo.proto"], &["src"])?;
Ok(())
}
build.rs
脚本会在你的 .proto
发生变化时运行。该脚本将在 OUT_DIR
中生成一个文件,其名称与 .proto
文件中的 package
字段相同(如果未声明,名称将为 '_.rs')。该文件将包含
- 在
.proto
中声明的所有消息作为 Rust 结构体。 *1 - (
#[cfg(feature = "server")]
) 一个名为{YOUR_RPC_SERVICE_NAME}Server
的特质,其中定义了服务端的方法。因此,您应该使用此特质来构建具有业务逻辑的实现。*2 - (
#[cfg(feature = "client")]
) 一个名为{YOUR_RPC_SERVICE_NAME}ClientDefinition<T: Transport + 'static>: ServiceClient<T> + Send + Sync + 'static
的特质,以及客户端的实现,命名为{YOUR_RPC_SERVICE_NAME}Client
。您可以在使用RpcClient
时使用此自动生成的实现,通过在load_module
函数中传递实现(实现了特质的结构体)作为泛型,它将负责请求您的服务的程序。但是,您也可以自行实现{YOUR_RPC_SERVICE_NAME}ClientDefinition
特质,只要实现满足特质的和RpcClient
的要求。*3 - (
#[cfg(feature = "server")]
) 当创建RpcServerPort
时,负责注册您声明的服务的一个结构体。您应该在创建RpcServer
端口处理程序内部使用此结构体及其注册函数。*4
为了导入它们,您必须添加
include!(concat!(env!("OUT_DIR"), "/decentraland.echo.rs"));
此语句应添加到 src/lib.rs
中,以便将自动生成的代码部分包含到您的包中,否则它将把每个导入视为不同的类型。
服务器端
use dcl_rpc::{
transports::web_socket::{WebSocketServer, WebSocketTransport},
server::{RpcServer, RpcServerPort},
service_module_definition::{Definition, ServiceModuleDefinition, CommonPayload}
};
use crate::{
EchoServiceRegistration, // (*4)
};
// Define the IP and Port where the WebSocket Server will run
let ws_server = WebSocketServer::new("localhost:8080");
// 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 {
hardcoded_database: create_db(),
};
let mut server = RpcServer::create(ctx);
server.set_handler(|port: &mut RpcServerPort<MyExampleContext>| {
// The EchoServiceRegistration will be autogenerated, so you'll need to define the echo_service, which will have all the behaviors of your service. Following the example, it'll have the logic for the `hello` message.
EchoServiceRegistration::register_service(port, echo_service::MyEchoService {})
});
// 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();
tokio::spawn(async move {
while let Some(Ok(connection)) = connection_listener.recv().await {
let transport = WebSocketTransport::new(connection);
match server_events_sender.send_attach_transport(transport) {
Ok(_) => {
println!("> RpcServer > transport attached successfully");
}
Err(_) => {
println!("> RpcServer > unable to attach transport");
panic!()
}
}
}
});
server.run().await;
为您服务实现特质
use crate::{
MyExampleContext,
EchoServiceServer, // (*2)
Text // (*1) message
};
pub struct MyEchoService;
#[async_trait::async_trait]
impl EchoServiceServer<MyExampleContext> for MyEchoService {
async fn hello(&self, request: Text, _ctx: Arc<MyExampleContext>) -> Text {
request
}
}
客户端
启动一个 WebSocket 客户端连接并向回声服务器发送一个 Hello World 消息。
use crate::{EchoServiceClient, RPCServiceClient} // (*3)
use dcl_rpc::{transports::web_socket::{WebSocketClient, WebSocketTransport}, client::RpcClient};
use ws_rust::Text;
let client_connection = WebSocketClient::connect("ws://127.0.0.1:8080")
.await
.unwrap();
let client_transport = WebSocketTransport::new(client_connection);
let mut client = RpcClient::new(client_transport).await.unwrap();
let port = client.create_port("echo").await.unwrap();
let module = port.load_module::<EchoServiceClient>("EchoService").await.unwrap();
let response = module.hello(Text { say_something: "Hello World!".to_string()}).await;
依赖项
~12–25MB
~363K SLoC