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 异步

Download history 44/week @ 2024-03-29 22/week @ 2024-04-05 11/week @ 2024-04-12 12/week @ 2024-04-19 21/week @ 2024-04-26 21/week @ 2024-05-03 14/week @ 2024-05-10 24/week @ 2024-05-17 51/week @ 2024-05-24 19/week @ 2024-05-31 13/week @ 2024-06-07 12/week @ 2024-06-14 15/week @ 2024-06-21 5/week @ 2024-06-28 35/week @ 2024-07-05 33/week @ 2024-07-12

89每月下载量
用于 2 crate

MIT 协议

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,transformbecome 类似。

示例

#[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