1 个不稳定版本
0.9.0 | 2020年1月20日 |
---|
#58 in #actor-framework
在 3 个crate中使用
225KB
5K SLoC
Actori
Actori 是一个 Rust Actor 框架。
- 用户指南
- API 文档(开发版)
- API 文档(发布版)
- Cargo 包:actori
- 最低支持的 Rust 版本:1.39 或更高
功能
- 异步/同步 Actor。
- 本地/线程上下文中的 Actor 通信。
- 使用 Futures 进行异步消息处理。
- 支持 HTTP1/HTTP2(actori-web)
- Actor 监督。
- 类型化消息(无
Any
类型)。
使用方法
要使用 actori
,请将以下内容添加到您的 Cargo.toml
[dependencies]
actori = "0.9"
初始化 Actori
为了使用 actori,您首先需要创建一个 System
。
fn main() {
let system = actori::System::new("test");
system.run();
}
Actori 使用 tokio 事件循环。 System::new()
创建一个新的事件循环并启动 System
Actor。 system.run()
启动 tokio 事件循环,并在 System
Actor 收到 SystemExit
消息时结束。
让我们创建一个简单的 Actor。
实现 Actor
为了定义一个actor,你需要定义一个struct,并让它实现Actor
特质。
use actori::{Actor, Addr, Arbiter, Context, System};
struct MyActor;
impl Actor for MyActor {
type Context = Context<Self>;
fn started(&mut self, ctx: &mut Self::Context) {
println!("I am alive!");
System::current().stop(); // <- stop system
}
}
fn main() {
let system = System::new("test");
let addr = MyActor.start();
system.run();
}
通过Actor特质的start
和create
方法可以创建一个新的actor。它提供了多种创建actor的方法,具体详情请查阅文档。你可以实现Actor特质的started
、stopping
和stopped
方法。started
在actor启动时被调用,而stopping
在actor结束时被调用。有关actor生命周期的更多信息,请查看API文档。
处理消息
Actor通过发送消息与其他Actor通信。在actori中,所有消息都是有类型的。让我们定义一个简单的带有两个usize
参数的Sum
消息,以及一个可以接收此消息并返回这两个数字之和的actor。
use futures::{future, Future};
use actori::*;
// this is our Message
struct Sum(usize, usize);
// we have to define the response type for `Sum` message
impl Message for Sum {
type Result = usize;
}
// Actor definition
struct Summator;
impl Actor for Summator {
type Context = Context<Self>;
}
// now we need to define `MessageHandler` for the `Sum` message.
impl Handler<Sum> for Summator {
type Result = usize; // <- Message response type
fn handle(&mut self, msg: Sum, ctx: &mut Context<Self>) -> Self::Result {
msg.0 + msg.1
}
}
fn main() {
let sys = System::new("test");
let addr = Summator.start();
let res = addr.send(Sum(10, 5)); // <- send message and get future for result
Arbiter::spawn(res.then(|res| {
match res {
Ok(result) => println!("SUM: {}", result),
_ => println!("Something wrong"),
}
System::current().stop();
future::result(Ok(()))
}));
sys.run();
}
所有与actor的通信都通过一个Addr
对象进行。你可以通过do_send
发送一个消息而不等待响应,或者通过send
发送一个带有特定消息的actor。《Message》特质定义了消息的结果类型。
Actor状态和特定消息的订阅
你可能已经注意到,Actor
和Handler
特质的方法定义了接受&mut self
,因此你可以在actor中存储任何内容,并在必要时对其进行修改。
地址对象需要一个actor类型,但如果我们只想向可以处理该消息的actor发送特定消息,我们可以使用Recipient
接口。让我们创建一个新的actor,该actor使用Recipient
。
use std::time::Duration;
use actori::prelude::*;
#[derive(Message)]
struct Ping { pub id: usize }
// Actor definition
struct Game {
counter: usize,
addr: Recipient<Ping>,
}
impl Actor for Game {
type Context = Context<Game>;
}
// simple message handler for Ping message
impl Handler<Ping> for Game {
type Result = ();
fn handle(&mut self, msg: Ping, ctx: &mut Context<Self>) {
self.counter += 1;
if self.counter > 10 {
System::current().stop();
} else {
println!("Ping received {:?}", msg.id);
// wait 100 nanos
ctx.run_later(Duration::new(0, 100), move |act, _| {
act.addr.do_send(Ping{id: msg.id + 1});
});
}
}
}
fn main() {
let system = System::new("test");
// To get a Recipient object, we need to use a different builder method
// which will allow postponing actor creation
let addr = Game::create(|ctx| {
// now we can get an address of the first actor and create the second actor
let addr = ctx.address();
let addr2 = Game{counter: 0, addr: addr.recipient()}.start();
// let's start pings
addr2.do_send(Ping{id: 10});
// now we can finally create first actor
Game{counter: 0, addr: addr2.recipient()}
});
system.run();
}
聊天示例
有一个聊天示例,它提供了一个基本的网络客户端/服务器服务示例。
fectl
你可以考虑查看fectl实用程序。它是用actori
编写的,展示了如何创建具有相对复杂交互的网络应用程序。
贡献
欢迎所有贡献,如果你有功能请求,请毫不犹豫地打开一个问题!
许可协议
本项目根据你的选择,许可协议为以下之一:
- Apache License,版本2.0,(LICENSE-APACHE或http://www.apache.org/licenses/LICENSE-2.0)
- MIT许可协议(LICENSE-MIT或http://opensource.org/licenses/MIT)
任选其一。
行为准则
对actori crate的贡献是在贡献者协约的条款下组织的,actori的维护者@fafhrd91承诺将介入维护该行为准则。
依赖项
~6–11MB
~197K SLoC