4 个版本
新版本 0.1.3 | 2024 年 8 月 23 日 |
---|---|
0.1.2 | 2024 年 8 月 22 日 |
0.1.1 | 2024 年 8 月 21 日 |
0.1.0 | 2024 年 8 月 1 日 |
#570 在 网页编程 中
每月 232 次下载
81KB
422 行
logimesh
logimesh
是一个受 迈向云应用程序的现代开发 论文启发的 Rust 微组件 2.0 框架。
(这是我一个业余的想法,仅在工作之余开发。)
logimesh 的一些特性
- 客户端同时支持本地调用和远程调用,这意味着用户可以根据上下文动态切换调用方法。
用法
将以下依赖项添加到您的 Cargo.toml
中
logimesh = "0.1"
logimesh::component
属性扩展为形成一个组件组件的项目集合。这些生成的类型使得编写更少模板的服务器变得简单且易于使用。只需实现生成的组件特质,您就可以开始比赛了!
示例
此示例使用 tokio,因此请将以下依赖项添加到您的 Cargo.toml
[lib]
name = "service"
path = "src/lib.rs"
...
[dependencies]
anyhow = "1.0"
futures = "0.3"
logimesh = { version = "0.1" }
tokio = { version = "1.0", features = ["macros"] }
有关更实际的示例,请参阅 logimesh-example。
首先,让我们设置依赖关系和组件定义。
lib.rs
文件
# extern crate futures;
use futures::{
prelude::*,
};
use logimesh::{
client, context,
server::{self, incoming::Incoming, Channel},
};
// This is the component definition. It looks a lot like a trait definition.
// It defines one RPC, hello, which takes one arg, name, and returns a String.
#[logimesh::component]
trait World {
/// Returns a greeting for name.
async fn hello(name: String) -> String;
}
此组件定义生成一个名为 World
的特质。接下来,我们需要为我们的 Server 结构体实现它。
# extern crate futures;
# use futures::{
# prelude::*,
# };
# use logimesh::{
# client, context,
# server::{self, incoming::Incoming},
# };
# // This is the component definition. It looks a lot like a trait definition.
# // It defines one RPC, hello, which takes one arg, name, and returns a String.
# #[logimesh::component]
# trait World {
# /// Returns a greeting for name.
# async fn hello(name: String) -> String;
# }
/// This is the type that implements the generated World trait. It is the business logic
/// and is used to start the server.
#[derive(Clone)]
struct CompHello;
impl World for CompHello {
// Each defined rpc generates an async fn that serves the RPC
async fn hello(self, _: context::Context, name: String) -> String {
format!("Hello, {name}!")
}
}
server.rs
文件
use clap::Parser;
use futures::future;
use futures::prelude::*;
use logimesh::server::incoming::Incoming;
use logimesh::server::{self, Channel};
use logimesh::tokio_serde::formats::Json;
use service::{CompHello, World};
use std::net::{IpAddr, Ipv6Addr};
#[derive(Parser)]
struct Flags {
/// Sets the port number to listen on.
#[clap(long)]
port: u16,
}
async fn spawn(fut: impl Future<Output = ()> + Send + 'static) {
tokio::spawn(fut);
}
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let flags = Flags::parse();
let server_addr = (IpAddr::V6(Ipv6Addr::LOCALHOST), flags.port);
// JSON transport is provided by the json_transport logimesh module. It makes it easy
// to start up a serde-powered json serialization strategy over TCP.
let mut listener = logimesh::serde_transport::tcp::listen(&server_addr, Json::default).await?;
println!("Listening on port {}", listener.local_addr().port());
listener.config_mut().max_frame_length(usize::MAX);
listener
// Ignore accept errors.
.filter_map(|r| future::ready(r.ok()))
.map(server::BaseChannel::with_defaults)
// Limit channels to 1 per IP.
.max_channels_per_key(2, |t| t.transport().peer_addr().unwrap().ip())
// serve is generated by the component attribute. It takes as input any type implementing
// the generated World trait.
.map(|channel| {
let server = CompHello;
channel.execute(server.logimesh_serve()).for_each(spawn)
})
// Max 10 channels.
.buffer_unordered(10)
.for_each(|_| async {})
.await;
Ok(())
}
client.rs
文件
use clap::Parser;
use logimesh::client::stub::LRConfig;
use logimesh::client::stub::TransportCodec::Json;
use logimesh::context;
use logimesh::discover::service_lookup_from_addresses;
use service::{CompHello, World as _};
use std::net::SocketAddr;
use std::time::Duration;
use tokio::time::sleep;
#[derive(Parser)]
struct Flags {
/// Sets the server address to connect to.
#[clap(long)]
server_addr: SocketAddr,
/// Sets the name to say hello to.
#[clap(long)]
name: String,
}
#[tokio::main]
async fn main() -> anyhow::Result<()> {
let flags = Flags::parse();
let client = CompHello
.logimesh_client(
LRConfig::new("p.s.m".into(),
service_lookup_from_addresses(vec![flags.server_addr.to_string()])).with_transport_codec(Json),
)
.await?;
let hello = client.hello(context::current(), format!("{}1", flags.name)).await;
match hello {
Ok(hello) => println!("{hello:?}"),
Err(e) => tracing::warn!("{:?}", anyhow::Error::from(e)),
}
Ok(())
}
依赖项
~8–10MB
~169K SLoC