Crate datacake_rpc
source ·Expand description
An actor-like RPC framework built for true zero-copy message handling.
This framework is inspired by tonic but is not a GRPC framework. Instead, it makes use of the incredible rkyv (de)serialization framework which provides us with lightning fast (de)serialization and also lets us perform true zero-copy deserialization which can lead to massive performance improvements when processing lots of big messages at once.
Features
- Fast (de)serialization of owned types.
- True zero-copy deserialization avoiding heavy allocations.
- Dynamic adding and removing of message handlers/services.
Basic example
use std::net::SocketAddr;
use datacake_rpc::{
Channel,
Handler,
Request,
RpcClient,
RpcService,
Server,
ServiceRegistry,
Status,
};
use rkyv::{Archive, Deserialize, Serialize};
// The framework accepts any messages which implement `Archive` and `Serialize` along
// with the archived values implementing `CheckBytes` from the `bytecheck` crate.
// This is to ensure safe, validated deserialization of the values.
//
// Checkout rkyv for more information!
#[repr(C)]
#[derive(Serialize, Deserialize, Archive, PartialEq, Debug)]
#[archive(compare(PartialEq), check_bytes)]
#[archive_attr(derive(PartialEq, Debug))]
pub struct MyMessage {
name: String,
age: u32,
}
pub struct MyService;
impl RpcService for MyService {
// The `register_handlers` is used to mark messages as something
// the given service can handle and process.
//
// Messages which are not registered will not be dispatched to the handler.
fn register_handlers(registry: &mut ServiceRegistry<Self>) {
registry.add_handler::<MyMessage>();
}
}
#[datacake_rpc::async_trait]
impl Handler<MyMessage> for MyService {
type Reply = String;
// Our `Request` gives us a zero-copy view to our message, this doesn't actually
// allocate the message type.
async fn on_message(&self, msg: Request<MyMessage>) -> Result<Self::Reply, Status> {
Ok(msg.to_owned().unwrap().name)
}
}
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let address = "127.0.0.1:8000".parse::<SocketAddr>()?;
let server = Server::listen(address).await?;
// Services can be added and removed at runtime once the server is started.
server.add_service(MyService);
println!("Listening to address {}!", address);
// Channels are cheap to clone similar to tonic.
let client = Channel::connect(address);
println!("Connected to address {}!", address);
let rpc_client = RpcClient::<MyService>::new(client);
let msg1 = MyMessage {
name: "Bobby".to_string(),
age: 12,
};
// Clients only need references to the message which helps
// reduce allocations.
let resp = rpc_client.send(&msg1).await?;
assert_eq!(resp, msg1.name);
Ok(())
}
Structs
- An archived
Status
- A wrapper type around the internal hyper::Body
- A raw client connection which can produce multiplexed streams.
- A block of data that can be accessed as if it is the archived value
T
. - The data provided is unable to be presented as the archived version of the view type.
- A zero-copy view of the message data and any additional metadata provided by the RPC system.
- A RPC client handle for a given service.
- A RPC server instance.
- A registry system used for linking a service’s message handlers with the RPC system at runtime.
- Status information around the cause of a message request failing.
Enums
- An archived
ErrorCode
- A failure in an RPC operation.
- A generic error code describing the high level reason why the request failed.
Traits
- A generic RPC message handler.
- The deserializer trait for converting the request body into the desired type specified by Self::Content.
- A standard RPC server that handles messages.
- The serializer trait for converting replies into hyper bodies using a reference to self.
- The serializer trait converting replies into hyper bodies.
Type Definitions
- A type alias for the returned data view of the RPC message reply.
Attribute Macros
- A re-export of the async-trait macro.