13个版本
0.4.2 | 2020年11月30日 |
---|---|
0.4.1 | 2020年6月19日 |
0.4.0 | 2020年5月7日 |
0.3.2 | 2019年8月25日 |
0.1.0 | 2018年7月17日 |
#939 in 异步
89每月下载量
用于 2 crate
11KB
Riker Patterns
概述
此crate提供了一组在actor系统中常用的模式。
目前仅包括Ask模式和一种提供transform!
宏的行为变更模式。
目标是添加更多模式,其中一些在流行的actor编程书籍中有很好的文档记录,例如 《使用Actor Model的响应式消息模式》。
模式
Ask
Ask模式允许actor将值发送到actor系统之外。值以Future
的形式传递。
让我们看看它是如何工作的:Cargo.toml
[dependencies]
riker = "0.3.0"
riker-patterns = "0.3.0"
main.rs
:
use futures::future::RemoteHandle;
use riker_patterns::ask::*;
struct MyActor;
impl Actor for MyActor {
type Msg = u32;
fn recv(&mut self,
ctx: &Context<Self::Msg>,
msg: Self::Msg,
sender: Sender) {
// sender is the Ask, waiting to a message to be sent back to it
sender.try_tell(msg * 2, Some(ctx.myself().into()));
}
}
fn main() {
let sys = ActorSystem::new().unwrap();
let props = Props::new(Box::new(MyActor::new));
let my_actor = sys.actor_of(props, "my-actor");
// ask returns a future that automatically is driven
// to completion by the system.
let res: RemoteHandle<u32> = ask(&sys, &my_actor, 100);
// the result future can be passed to a library or fuction that
// expects a future, or it can be extracted locally using `block_on`.
let res = block_on(res).unwrap();
println!("The result value is: {}", res);
}
在后台,Ask设置了一个临时的中间actor,该actor与ask的生命周期相同。其他actor将此临时actor视为sender
并将其消息发送回它。当临时的ask actor收到消息时,它将完成挂起的future并在自身上执行stop
以清理。
Ask特别适用于应用程序的部分在actor系统之外运行,或在另一个actor系统内运行的情况,例如作为API请求服务的web服务器(例如Hyper)。结果future可以作为未来堆栈的一部分进行链式调用。
Transform
Transform使得根据actor的当前状态改变actor行为变得更加容易理解。由于actor维护状态,并且确实是主要关注点,因此能够根据该状态以不同的方式处理消息非常重要。Transform模式通过为每个状态分配一个接收函数来分离消息处理。这避免了处理多个可能状态的过度match
,即处理行为在状态改变时预先处理,而不是在每次接收消息时处理。
!!! 信息 如果你熟悉 JVM 上的 Akka,transform
与 become
类似。
示例
#[macro_use]
use riker_patterns::transform::*;
impl ShoppingCart {
// created state
fn created(&mut self,
ctx: &Context<MyMsg>,
msg: MyMsg,
sender: Sender) {
match msg {
MyMsg::AddItem(item) => {
// add item to cart
...
}
MyMsg::Cancel => {
// future messages will be handled by `fn cancelled`
transform!(self, UserActor::cancelled);
}
MyMsg::Checkout => {
// future messages will be handled by `fn checkout`
transform!(self, UserActor::checkout);
}
}
}
// cancelled state
fn cancelled(&mut self,
ctx: &Context<MyMsg>,
msg: MyMsg,
sender: Sender) {
match msg {
MyMsg::AddItem(item) => {
// can't add item to cancelled cart
...
}
MyMsg::Cancel => {
// can't cancel an already cancelled cart
...
}
MyMsg::Checkout => {
// can't checkout out a canclled cart
...
}
}
}
// checkout state
fn checkout(&mut self,
ctx: &Context<MyMsg>,
msg: MyMsg,
sender: Sender) {
match msg {
MyMsg::AddItem(item) => {
// can't add item to cart during checkout
...
}
MyMsg::Cancel => {
// future messages will be handled by `fn cancelled`
transform!(self, UserActor::cancelled);
}
MyMsg::Checkout => {
// cart already in checkout process
...
}
}
}
}
impl Actor for ShoppingCart {
type Msg = MyMsg;
fn recv(&mut self,
ctx: &Context<Self::Msg>,
msg: Self::Msg,
sender: Sender) {
// just call the currently set transform function
(self.rec)(self, ctx, msg, sender)
}
}
transform!
宏期望当前接收函数在 self
上的字段名称为 rec
。使用不同的名称很简单,可以使用自己的宏,或者直接使用标准代码设置函数。使用 transform!
的优点是,它易于阅读和识别何时发生转换,因为它与标准代码明显不同。
依赖关系
~10MB
~197K SLoC