3个版本
0.1.2 | 2023年1月11日 |
---|---|
0.1.1 | 2022年12月25日 |
0.1.0 | 2022年12月25日 |
#1755 in 异步
26KB
400 行
Akt
Rust和Tokio的actor框架。
它深受Actix的启发,现在它与Actix非常相似。主要区别在于,在Actix中(至少在创建这个特定库的时候)处理async/await和异步处理器有点繁琐。这个库支持开箱即用的异步处理器,并提供工具来简化一些不寻常的通信方式,例如等待结果而不会阻塞主消息循环。
状态
这个库还处于开发初期阶段。这意味着
- 您可能会遇到大量错误
- API可能会在任何时候发生不兼容的更改
- 测试覆盖率不完整
然而,它已经在生产中使用,因此您也可以在您的项目中尝试使用它。
概述
让我们实现一些actor来管理银行账户。
use mpi_actors::{Actor, Context, Handler, Message};
use async_trait::async_trait;
use thiserror::Error;
// A state managed by our actor
struct Account {
balance: u32,
}
// Implementing Actor trait makes an actor out of the state
impl Actor for Account {}
// Define messages to be handled by the actor
struct Deposit {
pub amount: u32,
}
impl Message for Deposit {
// Result the actor will respond with
type Result = u32;
}
// Implement handler for the Deposit message and Account actor
// We need to use `async_trait` crate "magic" because async trait methods
// is not supported bu rust directly yet.
#[async_trait]
impl Handler<Deposit> for Account {
async fn handle(&mut self, message: Deposit, _context: &mut Context<Account>) -> u32 {
self.balance = self.balance + message.amount;
self.balance
}
}
struct Withdraw {
pub amount: u32,
}
// Withdrawal may fail so we could provide specific error for it.
//
// It could be a struct, but for backwards compatibility reasons it could
// be a good idea to make enum even if for now we have only one variant.
//
// Here we use `thiserror` crate for convenient Error
// derivation, but it is completely optional.
#[derive(Debug, Error, PartialEq)]
enum WithdrawalError {
#[error("Insufficient funds. {requested} was requested but only {available} is available.")]
InsufficientFunds { requested: u32, available: u32 },
}
impl Message for Withdraw {
type Result = Result<u32, WithdrawalError>;
}
#[async_trait]
impl Handler<Withdraw> for Account {
async fn handle(
&mut self,
message: Withdraw,
_context: &mut Context<Account>,
) -> Result<u32, WithdrawalError> {
if self.balance < message.amount {
return Err(WithdrawalError::InsufficientFunds {
requested: message.amount,
available: self.balance,
});
}
self.balance = self.balance - message.amount;
Ok(self.balance)
}
}
#[tokio::main]
async fn main() {
// Create actor
let actor = Account { balance: 50 };
// Run actor consuming it and returning its address
let address = actor.run();
// Send message and use regular `await` workflow to wait for the response.
let balance = address.send(Withdraw { amount: 20 }).await;
assert_eq!(balance, Ok(Ok(30)));
let balance = address.send(Deposit { amount: 40 }).await;
assert_eq!(balance, Ok(70));
let balance = address.send(Withdraw { amount: 100 }).await;
assert_eq!(
balance,
Ok(Err(WithdrawalError::InsufficientFunds {
requested: 100,
available: 70
}))
);
// Actors addresses could be safely cloned
let address_clone = address.clone();
let balance = address_clone.send(Withdraw { amount: 10 }).await;
assert_eq!(balance, Ok(Ok(60)));
let balance = address.send(Withdraw { amount: 10 }).await;
assert_eq!(balance, Ok(Ok(50)));
}
依赖项
~2.3–4MB
~65K SLoC