1 个不稳定版本

使用旧的 Rust 2015

0.3.0 2017 年 4 月 7 日

#1836异步

39 每月下载量

MIT 许可证

81KB
1.5K SLoC

RobotS

Build Status Coverage Status

Robots 是一个纯 Rust actor 系统库,它从 erlangakka 中汲取了大量灵感。

文档可以在 这里 找到。

构建它

此库可以在稳定版本的 rustc (从 1.4.0 开始) 上构建。

简单

cargo build

应该就足够了。

用法

初始化 ActorSystem

为了使用这个 crate,你首先需要创建一个 ActorSystem,并给它一些线程来工作。

extern crate robots;
use robots::actors::ActorSystem;

fn main() {
    let actor_system = ActorSystem::new("test".to_owned());
    actor_system.spawn_threads(1);
}

你还可以通过调用其上的 shutdown 方法来关闭 actor 系统,它将停止所有线程并终止所有 actor 及其分配的资源(如果你正确实现了它们的 drop)。

这很好,但你需要用一些 Actors 来填充它。

实现 Actor

为了定义一个 actor,你需要

  • 定义一个 struct 并使其实现 Actor trait。
  • 有一个接受单个参数的函数来创建它(如果需要多个值,可以使用 tuple,如果不需要任何值,可以使用 ())。
use robots::actors::{Actor, ActorCell, ActorContext};

struct Dummy;

impl Actor for Dummy {
    fn receive(&self, _message: Box<Any>, _context: ActorCell) {}
}

impl Dummy {
    fn new(_: ()) -> Dummy {
        Dummy
    }
}

实例化 Actor

现在让我们看看如何实例化这样的 actor。

actor 是使用 Props 创建的,这是一个包含此 actor 构造函数及其参数的结构。

有两种方式可以实例化一个 actor,它可以是另一个用户创建的 actor 的子代,或者用户 actor 的根 actor 的子代。

第一种方式看起来像

let props = Props::new(Arc::new(Dummy::new), ());
let greeter = context.actor_of(props, "dummy".to_owned());

第二种方式看起来像

let props = Props::new(Arc::new(Dummy::new), ());
let _actor = actor_system.actor_of(props, "dummy".to_owned());

actor_of 方法将为你提供创建的 actor 的 ActorRef

请注意,创建 actor 的第一种方式比第二种方式快得多(大约快 10 倍),因此只应在需要创建新的 actor 层级结构时使用。

处理消息

行为者将以其消息的形式接收 Box<Any>,这允许行为者处理多种类型的消息,这在许多情况下都非常有用(例如,转发消息)。

要从 Box<Any> 获得具体类型,您需要像以下示例那样进行向下转型。

impl Actor for Printer {
    fn receive(&self, message: Box<Any>, _context: ActorCell) {
        if let Ok(message) = Box::<Any>::downcast::<String>(message) {
            println!("{}", *message);
        }
    }
}

如您所见,这相当简单。

如果您认为使用 Box<Any> 非常糟糕,并且应该有人对我做些可怕的事情,请在 :)之前查看这篇帖子

ActorContext 方法

现在让我们看看如何使用上下文参数。

这提供了大多数期望在行为者中实现的通信方法和功能。

/// Returns an ActorRef to the Actor.
fn actor_ref(&self) -> ActorRef;

/// Spawns a child actor.
fn actor_of(&self, props: Arc<ActorFactory>, name: String) -> Result<ActorRef, &'static str>;

/// Sends a Message to the targeted ActorRef.
fn tell<MessageTo: Message>(&self, to: ActorRef, message: MessageTo);

/// Creates a Future, this Future will send the message to the targetted ActorRef (and thus be
/// the sender of the message).
fn ask<MessageTo: Message>(&self, to: ActorRef, message: MessageTo, future_name: String) -> ActorRef;

/// Completes a Future.
fn complete<MessageTo: Message>(&self, to: ActorRef, complete: MessageTo);

/// Tells a future to forward its result to another Actor.
/// The Future is then dropped.
fn forward_result<T: Message>(&self, future: ActorRef, to: ActorRef);

/// Tells a future to forward its result to another Future that will be completed with this
/// result.
/// The Future is then dropped.
fn forward_result_to_future<T: Message>(&self, future: ActorRef, to: ActorRef);

/// Sends the Future a closure to apply on its value, the value will be updated with the output
/// of the closure.
fn do_computation<T: Message, F: Fn(Box<Any + Send>, ActorCell) -> T + Send + Sync + 'static>
    (&self, future: ActorRef, closure: F);

/// Requests the targeted actor to stop.
fn stop(&self, actor_ref: ActorRef);

/// Asks the father of the actor to terminate it.
fn kill_me(&self);

/// Returns an Arc to the sender of the message being handled.
fn sender(&self) -> ActorRef;

/// Father of the actor.
fn father(&self) -> ActorRef;

/// Children of the actor.
fn children(&self) -> HashMap<Arc<ActorPath>, ActorRef>;

/// Lifecycle monitoring, list of monitored actors.
fn monitoring(&self) -> HashMap<Arc<ActorPath>, (ActorRef, FailureHandler)>;

/// Actors monitoring this actor.
fn monitored_by(&self) -> Vec<ActorRef>;

/// Monitor an actor with the given handler.
fn monitor(&self, actor: ActorRef, handler: FailureHandler);

/// Logical path to the actor, such as `/user/foo/bar/baz`
fn path(&self) -> Arc<ActorPath>;

/// Future containing an Option<ActorRef> with an ActtorRef to the Actor with the given logical
/// path.
///
/// The future will have the path: `$actor/$name_request`
fn identify_actor(&self, logical_path: String, request_name: String) -> ActorRef;

/// Sends a control message to the given actor.
fn tell_control(&self, actor: ActorRef, message: ControlMessage);

/// Puts the actor in a state of failure with the given reason.
fn fail(&self, reason: &'static str);

日志记录

RobotS 在 log crate 的 info 通道上记录信息。

如果您从未使用过此 crate,您可以通过将以下行添加到您的可执行文件中简单地激活日志记录。

extern crate env_logger;

// Your use and extern crates...

fn main() {
    env_logger::init().unwrap();

    // Your main code.
}

您需要将 RUST_LOG 环境变量设置为 robots=info。例如,在启动阶乘示例时,我们看到

➜  RobotS git:(master)RUST_LOG=robots=info cargo run --example=factorial
   Compiling RobotS v0.3.0 (file:///home/gamazeps/dev/RobotS)
     Running `target/debug/examples/factorial`
INFO:robots::actors::actor_system: Created cthulhu
INFO:robots::actors::actor_ref: /user receiving a system message
INFO:robots::actors::actor_system: Created /user actor
INFO:robots::actors::actor_ref: /system receiving a system message
INFO:robots::actors::actor_system: Created /system actor
INFO:robots::actors::actor_system: Launched the first thread
INFO:robots::actors::actor_system: Created the channel to get an ActorRef from a root actor
INFO:robots::actors::actor_ref: / is sending a message to /system
INFO:robots::actors::actor_ref: /system receiving a message
INFO:robots::actors::actor_ref: /user handling a message
INFO:robots::actors::actor_ref: /system handling a message
INFO:robots::actors::actor_ref: /system handling a message
INFO:robots::actors::actor_cell: creating actor /system/name_resolver
INFO:robots::actors::actor_ref: /system/name_resolver receiving a system message
INFO:robots::actors::actor_system: Created the /system/name_resolver actor
INFO:robots::actors::actor_ref: /system/name_resolver handling a message
INFO:robots::actors::actor_system: Created the channel to get an ActorRef from a root actor
INFO:robots::actors::actor_ref: / is sending a message to /user
INFO:robots::actors::actor_ref: /user receiving a message
INFO:robots::actors::actor_ref: /user handling a message
INFO:robots::actors::actor_cell: creating actor /user/sender
INFO:robots::actors::actor_ref: /user/sender receiving a system message
INFO:robots::actors::actor_ref: /system/name_resolver receiving a message
INFO:robots::actors::actor_ref: /user/sender handling a message
INFO:robots::actors::actor_system: Created the channel to get an ActorRef from a root actor
INFO:robots::actors::actor_ref: /system/name_resolver handling a message
INFO:robots::actors::actor_ref: / is sending a message to /user
INFO:robots::actors::actor_ref: /user receiving a message
....

这有点冗长,但允许您监控您程序的正确执行。

贡献

欢迎所有贡献,如果您有功能请求,请毫不犹豫地提出问题!

功能

  • 在本地上下文中进行的行为者通信。
  • 使用行为者层次结构进行的行为者监督(每个行为者监督其子行为者)。
  • 使用显式原因和处理程序处理故障。
  • 使用 Future 进行异步请求的 Ask 模式。
  • 名称解析(从逻辑路径中获取 ActorRef)。
  • 日志记录。

待办事项

  • 以透明的方式在网络中进行通信。
  • 调查性能以节省一些微秒。
  • 您的疯狂想法?

基准测试

为行为者创建和本地消息传递编写了一些原始基准测试。

以下是我在 Intel(R) Core(TM) i5-3317U CPU @ 1.70GHz 处理器上的结果。

test create_1000_actors                   ... bench:   3,341,549 ns/iter (+/- 173,594)
test send_1000_messages                   ... bench:   1,217,572 ns/iter (+/- 196,141)

尽管如此,我还没有设法在我的计算机上运行 akka,因此我没有可以与之基准测试的内容,所以您可以随意使用这些基准测试。

依赖项

~4.5MB
~85K SLoC