2 个不稳定版本
0.2.0 | 2023年1月20日 |
---|---|
0.1.0 | 2023年1月14日 |
#32 in #async-channel
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