1 个不稳定版本
使用旧的 Rust 2015
0.3.0 | 2017 年 4 月 7 日 |
---|
#1836 在 异步
39 每月下载量
81KB
1.5K SLoC
RobotS
Robots 是一个纯 Rust actor 系统库,它从 erlang 和 akka 中汲取了大量灵感。
文档可以在 这里 找到。
构建它
此库可以在稳定版本的 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