#elixir #actors #async #actor-framework #erlang

nightly genserver

genserver 是一个受 Elixir 启发的异步演员库

10 个版本

0.2.2 2023 年 9 月 5 日
0.1.15 2022 年 9 月 14 日
0.1.14 2022 年 8 月 16 日

674并发 中排名

每月下载量 43
用于 gwyh

MIT 许可证

28KB
99

Docs Crates.io Build & test Codecov

genserver:生成通用服务器

genserver 是一个小巧的异步演员框架,灵感来自 Elixir 的 GenServer。用很少的代码,我们可以做很多事情。

查看 文档 获取更多详情。

如果你需要一个小巧的异步演员框架,不需要繁琐的功能,这个 crate 是一个很好的选择。这个 crate 的额外优势是速度非常快,并带有 Rust 的所有安全功能。

此代码 100% 纯 Rust,没有任何不安全代码。它具有最少的依赖项,以缩短编译时间。

使用 GenServer 特性创建和启动的每个服务器都会为其生成自己的循环来处理传入的消息。你可以创建尽可能多的服务器,它们可以通过保留使用 #[make_registry{}] 生成的注册表的副本相互通信。注册表可以克隆到不同的线程中,并且可以在同一运行时内安全地发送消息。

概述

首先,将 crate 添加为依赖项

cargo add genserver

然后,在你的 crate 中尝试这里的代码(注意在 main.rslib.rs 中启用这两个功能,分别用于应用程序和库)

// these two features must be enabled at the crate level
#![feature(type_alias_impl_trait, impl_trait_in_assoc_type)]

use std::future::Future;

use genserver::{make_registry, GenServer};

struct MyServer {
    // Any state your server needs can go right
    // in here. We keep a registry around in case
    // we want to call any other servers from our
    // server.
    registry: MyRegistry,
}

impl GenServer for MyServer {
    // Message type for this server.
    type Message = String;
    // The type of the registry defined by the `make_registry` attribute macro.
    type Registry = MyRegistry;
    // The response type for calls.
    type Response = String;

    // The call response type, a future, which returns Self::Response.
    type CallResponse<'a> = impl Future<Output = Self::Response> + 'a;
    // The cast response type, also a future, which returns unit.
    type CastResponse<'a> = impl Future<Output = ()> + 'a;

    fn new(registry: Self::Registry) -> Self {
        // When are server is started, the registry passes a copy
        // of itself here. We keep it around so we can call
        // other servers from this one.
        Self { registry }
    }

    // Calls to handle_call will block until our `Response` is returned.
    // Because they return a future, we can return an async block here.
    fn handle_call(&mut self, message: Self::Message) -> Self::CallResponse<'_> {
        println!("handle_call received {}", message);
        std::future::ready("returned from handle_call".into())
    }

    // Casts always return (), because they do not block callers and return
    // immediately.
    fn handle_cast(&mut self, message: Self::Message) -> Self::CastResponse<'_> {
        println!("handle_cast received {}", message);
        std::future::ready(())
    }
}

#[make_registry{
    myserver: MyServer
}]
struct MyRegistry;

// When the registry goes out of scope, everything will shut down. You have to
// keep the registry in scope as long as you want it to continue running.
tokio::spawn(async {
    let registry = MyRegistry::start().await;

    let response = registry
        .call_myserver("calling myserver".into())
        .await
        .unwrap();
    registry
        .cast_myserver("casting to myserver".into())
        .await
        .unwrap();
    
    // Give some time for the messages to be delivered.
    tokio::time::sleep(Duration::from_secs(1)).await;
});

依赖项

~2.3–4MB
~65K SLoC