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网页编程

Download history 92/week @ 2024-07-26 26/week @ 2024-08-02 114/week @ 2024-08-16

每月 232 次下载

MIT 许可证

81KB
422

logimesh

logimesh 是一个受 迈向云应用程序的现代开发 论文启发的 Rust 微组件 2.0 框架。

(这是我一个业余的想法,仅在工作之余开发。)

Crates.io Documentation License

component

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