Expand description
Distributed stateful services inspired by Orleans
This crate provides a framework for scalable, distributed and stateful services based on message passing between objects
§Application
Most of your application code will be written in forms of ServiceObjects
and Messages
use async_trait::async_trait;
use rio_rs::prelude::*;
use serde::{Deserialize, Serialize};
use std::sync::Arc;
#[derive(TypeName, Message, Deserialize, Serialize)]
pub struct HelloMessage {
pub name: String
}
#[derive(TypeName, Message, Deserialize, Serialize)]
pub struct HelloResponse {}
#[derive(TypeName, WithId, Default)]
pub struct HelloWorldService {
pub id: String,
}
#[async_trait]
impl Handler<HelloMessage> for HelloWorldService {
type Returns = HelloResponse;
type Error = NoopError;
async fn handle(
&mut self,
message: HelloMessage,
app_data: Arc<AppData>,
) -> Result<Self::Returns, Self::Error> {
println!("Hello world");
Ok(HelloResponse {})
}
}
§Running Server
To run your application you need to spin up your servers, the Server
TODO: Include example of other databases
use rio_rs::prelude::*;
use rio_rs::cluster::storage::sqlite::SqliteMembershipStorage;
use rio_rs::object_placement::sqlite::SqliteObjectPlacement;
#[tokio::main]
async fn main() {
let addr = "0.0.0.0:0";
// Configure types on the server's registry
let mut registry = Registry::new();
registry.add_type::<HelloWorldService>();
registry.add_handler::<HelloWorldService, HelloMessage>();
// Configure the Cluster Membership provider
let pool = SqliteMembershipStorage::pool()
.connect("sqlite::memory:")
.await
.expect("Membership database connection failure");
let members_storage = SqliteMembershipStorage::new(pool);
let membership_provider_config = PeerToPeerClusterConfig::default();
let membership_provider =
PeerToPeerClusterProvider::new(members_storage, membership_provider_config);
// Configure the object placement
let pool = SqliteMembershipStorage::pool()
.connect("sqlite::memory:")
.await
.expect("Object placement database connection failure");
let object_placement_provider = SqliteObjectPlacement::new(pool);
// Create the server object
let mut server = Server::new(
addr.to_string(),
registry,
membership_provider,
object_placement_provider,
);
server.prepare().await;
let listener = server.bind().await.expect("Bind");
// Run the server
// server.run(listener).await;
}
§Client
Communicating with the cluster is just a matter of sending the serialized known messages via TCP.
The client
module provides an easy way of achieving this:
use rio_rs::prelude::*;
use rio_rs::cluster::storage::sqlite::SqliteMembershipStorage;
#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
// Member storage configuration (Rendezvous)
let pool = SqliteMembershipStorage::pool()
.connect("sqlite::memory:")
.await?;
let members_storage = SqliteMembershipStorage::new(pool);
// Create the client
let mut client = ClientBuilder::new()
.members_storage(members_storage)
.build()?;
let payload = HelloMessage { name: "Client".to_string() };
let response: HelloResponse = client
.send::<HelloResponse, NoopError>(
"HelloWorldService".to_string(),
"any-string-id".to_string(),
&payload,
).await?;
// response is a `HelloResponse {}`
Ok(())
}
Re-exports§
pub use service_object::*;
Modules§
- app_
data - Shared data for Rio applications
- client
- Talk to a rio-rs server
- cluster
- This module holds all the modules responsible for clustering support
- derive
- Re-exports of rio_macros
- errors
- Repository of all error types for this crate using thiserror
- message_
router - Maps objects and their ids to different broadcast channels
- object_
placement - Maps object’s location in the cluster
- prelude
- protocol
- Client/Server communication protocol
- registry
- Trait object registry
- server
- Rio server
- service
- Server services
- service_
object - Module for implementing a Rio service
- sql_
migration - state
- Module that provides object persistence