#async-client #multi-threading #handle #async-channel #macro #resources #thread

bin+lib client-handle

一个用于在多线程/异步代码中生成客户端句柄的宏

2 个不稳定版本

0.2.0 2023年1月20日
0.1.0 2023年1月14日

#32 in #async-channel

MIT 协议

9KB

client-handle

编写多线程/异步代码的一个常见模式是允许线程/任务拥有资源,并通过通道发送消息来访问它。例如,从 tokio redis 示例: https://tokio.rs/tokio/tutorial/channels

该模式大致如下

  • 创建一个消息枚举。
  • 创建一个通道
  • 派生一个后台任务从 rx 接收器中读取。
  • 从一个或多个 tx 发送器发送消息
  • 使用与消息一起发送的单次通道来返回响应

为了提供一个易用的句柄,我也常常将 tx 发送器包装起来,并复制所有客户端函数。如下所示,这导致了大量的模板代码。

// Generate the message enum
enum Command {
    Get {
        reponse: oneshot::Sender<String>,
        key: String,
    }
}

// Create a channel and
// Spawn a receiver task
let (tx, mut rx) = mpsc::channel(32);
tokio::spawn(async move {
    while let Some(cmd) = rx.recv().await {
    use Command::*;

    match cmd {
        Get { reponse, key } => {
            let value = get_value(&key).await;
            let _ = response.send(value);
        }
    }
}

// Send messages to the channel using an ergonic client
struct Handle {
    tx: mpsc::Sender<Command>,
}

impl Handle {
    async fn get(&self, key: &String) {
        let (resp_tx, resp_rx) = oneshot::channel();
        let cmd = Command::Get {
            key: key.to_string(),
            resp: resp_tx,
        };

        // Send the GET request
        tx.send(cmd).await.unwrap();

        // Await the response
        let res = resp_rx.await;
        println!("GOT = {:?}", res);
    }
}

问题中的模板代码是在以下方面的重复

  • 接收代码解包消息并调用实际实现
  • 枚举的定义
  • 客户端句柄的实现

应该只提供上述代码的一部分,并推导出其他部分。这就是 client-handle 的作用,它将根据接收代码必须遵守的特性行为推导消息格式。

简而言之,上面的代码可以替换为以下代码

use client_handle::async_tokio_handle;

#[async_tokio_handle]
trait KvCommand {
    fn get(&self, key: String) -> String {
        self.get_value(&key)
    }
}

并且它可以这样使用

// create a struct for the trait
struct KvReceiver { /* data owned by the receiver */ };

impl KvCommand for KvReceiver {
    // Nothing to do here as the trait has default implemenations
}

#[tokio::main]
async fn main() {
    let receiver = KvReceiver;
    let handle = receiver.to_async_handle();
    let result = handle.get("foo".to_string()).await;
}

软件包特性

  • tokio - 启用使用 async_tokio_handle 宏。

代码中还有其他示例。有关生成的代码的完整细节,请参阅 client-handle-core 软件包中的单元测试。

为什么创建一个同步特性?

选择将宏放在特性上,以下是一些原因

  • 装饰枚举将涉及让用户创建“魔法字符串”作为返回值。
  • 使用特性允许使用像 automock 这样的工具进行测试

致谢

请参阅 注释 文件,了解用于创建此过程宏的资源。

依赖项

~1.8–8MB
~66K SLoC