# Actix Basics
Actix: 一个长期霸榜的web框架
TechEmpower Web Framework Performance Comparison
Actix Web lets you quickly and confidently develop web services in Rust and this guide get you going in no time.
The documentation on this website focusses primarily on the Actix Web framework. For information about the actor framework called Actix, check out the Actix book (or the lower level actix API docs). Otherwise, head on to the getting started guide. If you already know your ways around and you need specific information you might want to read the actix-web API docs.
特性:
- 异步/同步 actors
- Actor 在本地/线程上下文中通信
- 使用 Futures 进行异步消息处理
- Actor 监控
- 支持 HTTP1/HTTP2
- 类型化消息 (No Any type)
官网:
# Actix Quickstart
Before you can start writing an actix application, you’ll need a version of Rust installed. We recommend you use rustup to install or configure such a version.
# Install Rust
Before we begin, we need to install Rust using the rustup installer:
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
If you already have rustup installed, run this command to ensure you have the latest version of Rust:
rustup update
The actix framework requires Rust version 1.40.0 and up.
通过下面命令检测rust版本:
rustc --version
# Getting Started
Let’s create and run our first actix application. We’ll create a new Cargo project that depends on actix and then run the application.In previous section we already installed required rust version. Now let's create new cargo projects.
首先我们需要创建一个二进制项目,通过下面命令
cargo new actor-ping
cd actor-ping
同时需要添加actix依赖,通过在Cargo.toml中添加:
[dependencies]
actix = "0.10.0"
actix-rt = "1.1" # <-- Runtime for actix,actix的运行时,基于Tokio-based的单线程异步运行时
Actor Model
Actor模式是一种并发模型,与另一种模型共享内存完全相反,Actor模型share nothing。所有的线程(或进程)通过消息传递的方式进行合作,这些线程(或进程)称为Actor。共享内存更适合单机多核的并发编程,而且共享带来的问题很多,编程也困难。随着多核时代和分布式系统的到来,共享模型已经不太适合并发编程,因此几十年前就已经出现的Actor模型又重新受到了人们的重视。MapReduce就是一种典型的Actor模式,而在语言级对Actor支持的编程语言Erlang又重新火了起来,Scala也提供了Actor,但是并不是在语言层面支持,Java也有第三方的Actor包,Go语言channel机制也是一种类Actor模型。
参考:
编辑actor_ping/src/main.rs
,让我们创建一个actor
,它将接受Ping消息并以处理的Ping数量进行响应。
use actix::prelude::*;
/// Define `Ping` message
//#[derive(Message)] // 实现message的方式一,使用注解
//#[rtype(result = "usize")]
struct Ping(usize);
// 定义actor需要接收的消息,这里使员工ping就是为了让其接收消息,Message的主要目的是定义放回结果的类型
// Ping消息定义了usize,它指示任何可以接受Ping消息的参与者都需要返回usize值。
impl Message for Ping { // 实现message的方式二,使用trait
type Result = usize; // 定义消息的类型为usize
}
/// Actor
struct MyActor { // 创建一个actor
count: usize,
}
/// Declare actor and its context
//每个actor都有一个执行上下文,对于MyActor我们将使用Context <A>,下一部分将提供有关参与者上下文的更多信息。
impl Actor for MyActor { // 此actor必须实现Actor trait
type Context = Context<Self>; // 指定关联类型是Context<Self>
}
/// Handler for `Ping` message,必须实现 Handler<Ping> 的trait
impl Handler<Ping> for MyActor { // 指定RHS类型参数为Ping,表示Ping结构体类型和MyActor有操作
type Result = usize;
fn handle(&mut self, msg: Ping, _: &mut Context<Self>) -> Self::Result {
self.count += msg.0;
self.count
}
}
#[actix_rt::main]
async fn main() {
// 现在我们可以启动actor并发送message给它,启动过程其实应该取决于actor的上下文实现,在我们的例子中,我们可以使用基于<tokio/future的Context<A>
// start()和create()方法返回一个actor的地址
let addr = MyActor { count: 10 }.start(); // 我们可以使用Actor::start() 或 Actor::create()启动它,
// 第一个用于可以立即创建actor实例的情况,第二个用于创建actor之前访问上下文
// send message and get future for result,所有的actor的通过地址交流
let res = addr.send(Ping(10)).await; // 您可以不等待响应就发送一条消息,也可以将特定的消息发送给actor。
// handle() returns tokio handle
// 在这里,我们使用actix-rt作为启动系统和驱动主要Future的方式,因此我们可以轻松地等待发送给Actor的消息
println!("RESULT: {}", res.unwrap() == 20);
// stop system and exit
System::current().stop();
}
# Actor
Actix is built on the Actor Model which allows applications to be written as a group of independently executing but cooperating "Actors" which communicate via messages. Actors are objects which encapsulate state and behavior and run within the Actor System provided by the actix library.
Actix基于Actor模型构建,该模型允许将应用程序编写为一组通过消息进行通信的独立执行但相互协作的“ Actor”。Actor是封装状态和行为并在actix库提供的Actor系统中运行的对象。(Actor=状态+行为)
# Actor lifecycle
# Started
一个Actor总是在处于Started
状态下开始,在这个状态区间中表示Actor的started()
方法被调用,Actor trait提供了此方法的默认实现,Actor上下文在此状态下可用,并且Actor可以启动更多Actor、注册异步流或进行任何其他所需的配置。
# Running
在Actor的starts()
方法被调用之后,Actor转换为Running状态,并且Actor可以无限期保持运行状态。
# Stopping
在以下情况下,Actor的执行状态变为停止状态:
Context::stop
is called by the actor itself. Actor本身调用停止- all addresses to the actor get dropped. i.e. no other actor references it. 在没有其他Actor引用它的前提下丢弃所有的Actor
- no event objects are registered in the context. 在上下文中没有注册任何事件对象
Actor可以从stopping状态恢复到running状态,通过创建新地址或添加事件对象并通过Running::Continue
返回,如果一个Actor由于调用Context::stop()
而将状态更改为stopping,则上下文会立即停止处理的message
并调用Actor::stopping()
。如果Actor没有恢复到running状态,则会丢弃所有未处理的消息。By default this method returns Running::Stop
which confirms the stop operation.
# Stopped
If the actor does not modify the execution context during the stopping state, the actor state changes to Stopped. This state is considered final and at this point the actor is dropped.
# Message
Actor通过发送message与其他Actor交流,在Actix中,所有message都是可以被自定义的,一条message可以是所有实现了Message trait的rust类型。Message::Result
定义返回类型,让我们定义一个简单的Ping消息-接受此消息的Actor需要返回Result <bool,std::io::Error>
。
extern crate actix;
use actix::prelude::*;
struct Ping;
impl Message for Ping {
type Result = Result<bool, std::io::Error>;
}
fn main() {}
# Spawning an actor
如何创建一个Actor取决于依赖的上下文,通过Actor特性的start
和create
方法可以生成新的异步Actor,It provides several different ways of creating actors。
# Complete example
use actix::prelude::*;
/// Define message
#[derive(Message)]
#[rtype(result = "Result<bool, std::io::Error>")] // 使用注解的宏属性定义Message的返回类型
struct Ping;
// Define actor
struct MyActor;
// Provide Actor implementation for our actor
impl Actor for MyActor { // 定义实现Actor trait的MyActor,首先需要Context,同时在started中会在此状态中执行
type Context = Context<Self>;
fn started(&mut self, ctx: &mut Context<Self>) {
println!("Actor is alive");
}
fn stopped(&mut self, ctx: &mut Context<Self>) {
println!("Actor is stopped");
}
}
/// Define handler for `Ping` message
impl Handler<Ping> for MyActor {
type Result = Result<bool, std::io::Error>; // 定义返回类型的实现
fn handle(&mut self, msg: Ping, ctx: &mut Context<Self>) -> Self::Result {
println!("Ping received"); // 处理send发送的消息
Ok(true)
}
}
#[actix_rt::main]
async fn main() {
// Start MyActor in current thread
let addr = MyActor.start();
// Send Ping message.
// send() message returns Future object, that resolves to message result
let result = addr.send(Ping).await; // 此方法是基于MyActor的所以会自动传入self和一个Ping类型,触发Handler trait
match result {
Ok(res) => println!("Got result: {}", res.unwrap()), // Got result: true
Err(err) => println!("Got error: {}", err),
}
}
# Responding with a MessageResponse
让我们看看上面示例中为impl Handler
定义的Result
类型,看看我们是如何返回Result<bool, std::io::Error>
的,我们能够以这种类型响应Actor的传入消息,因为它具有针对该类型实现的MessageResponse trait,Here's the definition for that trait:
// 在Hanler tarit定义中的要求:type Result: MessageResponse<Self, M>; 这里的M对应于Result<bool, std::io::Error>
pub trait MessageResponse<A: Actor, M: Message> {
fn handle<R: ResponseChannel<M>>(self, ctx: &mut A::Context, tx: Option<R>);
}
有时候响应没有传入实现了这类tarit的类型的message也是很有意义的,当遇到这类情况时,我们可以自己实现此类trait,这是一个示例,其中我们用GotPing响应Ping消息,然后用GotPong响应Pong消息。
use actix::dev::{MessageResponse, ResponseChannel};
use actix::prelude::*;
#[derive(Message)]
#[rtype(result = "Responses")] // 定义响应类型
enum Messages {
Ping,
Pong,
}
enum Responses {
GotPing,
GotPong,
}
impl<A, M> MessageResponse<A, M> for Responses
where
A: Actor,
M: Message<Result=Responses>,
{
fn handle<R: ResponseChannel<M>>(self, _: &mut A::Context, tx: Option<R>) { // 这里的handle的ResponseChannel会接收实现的MyActor的handle的返回值
if let Some(tx) = tx {
tx.send(self); // 处理返回值
}
}
}
// Define actor
struct MyActor;
// Provide Actor implementation for our actor
impl Actor for MyActor {
type Context = Context<Self>;
fn started(&mut self, _ctx: &mut Context<Self>) {
println!("Actor is alive");
}
fn stopped(&mut self, _ctx: &mut Context<Self>) {
println!("Actor is stopped");
}
}
/// Define handler for `Messages` enum
impl Handler<Messages> for MyActor {
type Result = Responses; // 这里我们使用的是我们自己定义的Responses类型,而不是Result(会调用默认的MessageResponse)
fn handle(&mut self, msg: Messages, _ctx: &mut Context<Self>) -> Self::Result {
match msg {
Messages::Ping => Responses::GotPing,
Messages::Pong => Responses::GotPong,
}
}
}
#[actix_rt::main]
async fn main() {
// Start MyActor in current thread
let addr = MyActor.start();
// Send Ping message.
// send() message returns Future object, that resolves to message result
let ping_future = addr.send(Messages::Ping).await; // 先调用MyActor的handle -》 MessageResponse的handle
let pong_future = addr.send(Messages::Pong).await;
match pong_future {
Ok(res) => match res {
Responses::GotPing => println!("Ping received"),
Responses::GotPong => println!("Pong received"),
},
Err(e) => println!("Actor is probably dead: {}", e),
}
match ping_future {
Ok(res) => match res {
Responses::GotPing => println!("Ping received"),
Responses::GotPong => println!("Pong received"),
},
Err(e) => println!("Actor is probably dead: {}", e),
}
}
# Address
Actor仅通过交换消息进行通信,发送方可以选择等待响应,无法仅通过其address直接引用Actor。有几种获取演员地址的方法,Actor trait 提供了两种方法去开启Actor,同时返回一个开始状态阿德Actor的地址。
这是Actor::start()
方法用法的示例,在此示例中MyActor
结构体的Actor是异步的,并且在与调用方相同的线程中启动。一个异步Actor可以从Context结构中获取其地址,上下文需要实现AsyncContext
trait, AsyncContext::address()
提供Actor的地址。
use actix::prelude::*;
struct MyActor;
impl Actor for MyActor {
type Context = Context<Self>;
fn started(&mut self, ctx: &mut Context<Self>) {
let addr = ctx.address(); // 返回值
}
}
let addr = MyActor.start();
# Message
为了能够处理特定的消息,Actor必须为此message提供Handler <M>
实现,所有message都是静态键入的,消息可以异步方式处理。Actor可以生成其他Actor,也可以将 futures or streams
到执行上下文中。Actor特质提供了几种方法,可以控制Actor的生命周期。
要将消息发送给参与者,需要使用Addr对象,Addr提供了几种发送消息的方法:
Addr::do_send(M)
- this method ignores any errors in message sending. If the mailbox is full the message is still queued, bypassing the limit. If the actor's mailbox is closed, the message is silently dropped. This method does not return the result, so if the mailbox is closed and a failure occurs, you won't have an indication of this.(忽略发送中的错误,比如mailbox已满,message仍在排队,绕过所有限制,如果actor的mailbox已经关闭,则将message丢弃)Addr::try_send(M)
- this method tries to send the message immediately. If the mailbox is full or closed (actor is dead), this method returns a[SendError](https://actix.rs/actix/actix/prelude/enum.SendError.html)
. (立即发送消息,如果mailbox已满或者关闭,返回一个sendErr)Addr::send(M)
- This message returns a future object that resolves to a result of a message handling process. If the returnedFuture
object is dropped, the message is cancelled. (该方法返回一个future的对象,该对象解析为message的处理过程的结果,如果丢弃返回的future对象,则该消息也被取消)
# Recipient
收件人是地址的专用版本,仅支持一种类型的消息,如果消息需要发送到其他类型的Actor,则可以使用它,可以使用Addr::recipient()
从地址创建收件人对象。地址对象需要一个Actor类型,但是如果我们只想发送特定的消息给可以处理该消息的Actor,则可以使用收件人接口。
例如,收件人可以用于订阅系统,在以下示例中,OrderEvents
Actor向所有订阅者发送一个OrderShipped
消息,订户可以是实现Handler <OrderShipped>
特性的任何Actor。
use actix::prelude::*;
#[derive(Message)]
#[rtype(result = "()")]
struct OrderShipped(usize); // 寄件货运单号
#[derive(Message)]
#[rtype(result = "()")]
struct Ship(usize); // 寄件编号
/// Subscribe to order shipped event. 订阅订单发货事件。
#[derive(Message)]
#[rtype(result = "()")]
struct Subscribe(pub Recipient<OrderShipped>); // 这里的Subscribe是一些Actor的接收对象的地址,下面的Email、SMS的地址可以存放
/// Actor that provides order shipped event subscriptions, 提供订单发货事件订阅的Actor
struct OrderEvents {
subscribers: Vec<Recipient<OrderShipped>>, // 记录订阅者的Actor的接收地址
}
impl OrderEvents {
fn new() -> Self {
OrderEvents {
subscribers: vec![]
}
}
}
impl Actor for OrderEvents {
// 订单事件的Actor
type Context = Context<Self>;
}
impl OrderEvents {
/// Send event to all subscribers
fn notify(&mut self, order_id: usize) { // 提供订单事件通知功能,给订单订阅的对象发送消息
for subscr in &self.subscribers {
subscr.do_send(OrderShipped(order_id)).unwrap();
}
}
}
/// Subscribe to shipment event 订阅货运事件
impl Handler<Subscribe> for OrderEvents {
type Result = ();
fn handle(&mut self, msg: Subscribe, _: &mut Self::Context) {
self.subscribers.push(msg.0); // 订阅货运单号
}
}
/// Subscribe to ship message 订阅寄件留言
impl Handler<Ship> for OrderEvents {
type Result = ();
fn handle(&mut self, msg: Ship, _ctx: &mut Self::Context) -> Self::Result {
self.notify(msg.0); // 将寄件给订单事件,此时订单事件会通知每个订阅者,do_send,从而触发一系列动作
System::current().stop();
}
}
/// Email Subscriber
struct EmailSubscriber; //邮件订阅
impl Actor for EmailSubscriber {
type Context = Context<Self>;
}
// 得到通知后,触发handle函数
impl Handler<OrderShipped> for EmailSubscriber {
type Result = ();
fn handle(&mut self, msg: OrderShipped, _ctx: &mut Self::Context) -> Self::Result {
println!("Email sent for order {}", msg.0)
}
}
struct SmsSubscriber;
impl Actor for SmsSubscriber {
type Context = Context<Self>;
}
impl Handler<OrderShipped> for SmsSubscriber {
type Result = ();
fn handle(&mut self, msg: OrderShipped, _ctx: &mut Self::Context) -> Self::Result {
println!("SMS sent for order {}", msg.0)
}
}
fn main() {
let system = System::new("events");
let email_subscriber = Subscribe(EmailSubscriber {}.start().recipient());
let sms_subscriber = Subscribe(SmsSubscriber {}.start().recipient());
let order_event = OrderEvents::new().start();
order_event.do_send(email_subscriber); // 添加Actor订阅着接收地址到subscribe的vec![]中
order_event.do_send(sms_subscriber); // 触发impl Handler<Subscribe> for OrderEvents
order_event.do_send(Ship(1)); // 产生订单后,通知到各个订阅者,同时执行各自的handle方法,触发:impl Handler<Ship> for OrderEvents
system.run().unwrap();
}
# Context
Actors都维护一个内部执行上下文或状态,这样,Actor可以确定自己的地址,更改邮箱限制或停止执行等操作了。
# Mailbox
所有消息都首先发送到Actor的mailbox中,然后Actor的执行上下文调用特定的message处理程序,通常来说mailbox是有限制的,这也限制了上下文的实现。对于Context类型,默认情况下容量设置为16条消息,可以使用Context::set_mailbox_capacity()
增加容量,但请记住这不适用于绕过邮箱队列限制的Addr::do_send(M)
或完全绕过邮箱的AsyncContext::notify(M)
和AsyncContext::notify_later(M,Duration)
。
use actix::prelude::*;
struct MyActor;
impl Actor for MyActor {
type Context = Context<Self>;
fn started(&mut self, ctx: &mut Self::Context) {
ctx.set_mailbox_capacity(1);
}
}
fn main() {
System::new("test");
let addr = MyActor.start();
}
# Getting your actors Address
Actor可以从上下文中查看自己的地址,也许您想重新排队一个事件以供以后使用,或者您想要转换消息类型,也许您想用您的地址回复邮件,如果您希望Actor发送消息给自己,请看看AsyncContext::notify(M)
。
use actix::prelude::*;
struct MyActor;
struct WhoAmI;
impl Message for WhoAmI {
type Result = Result<actix::Addr<MyActor>, ()>;
}
impl Actor for MyActor {
type Context = Context<Self>;
}
impl Handler<WhoAmI> for MyActor {
type Result = Result<actix::Addr<MyActor>, ()>;
fn handle(&mut self, msg: WhoAmI, ctx: &mut Context<Self>) -> Self::Result {
Ok(ctx.address())
}
}
fn main() {
System::new("scratch");
let addr = MyActor.start();
let who_addr = addr.do_send(WhoAmI{});
}
# Stopping an Actor
在Actor执行上下文中,您可以选择阻止Actor处理任何将来的boxmail message,这可能是对错误情况的响应,或者是程序关闭的一部分,为此您可以调用Context::stop()
。
use actix::prelude::*;
struct MyActor {
count: usize,
}
impl Actor for MyActor {
type Context = Context<Self>;
}
#[derive(Message)]
#[rtype(result = "usize")]
struct Ping(usize);
impl Handler<Ping> for MyActor {
type Result = usize;
fn handle(&mut self, msg: Ping, ctx: &mut Context<Self>) -> Self::Result {
self.count += msg.0;
if self.count > 5 {
println!("Shutting down ping receiver.");
ctx.stop()
}
self.count
}
}
#[actix_rt::main]
async fn main() {
// start new actor
let addr = MyActor { count: 10 }.start();
// send message and get future for result
let addr_2 = addr.clone();
let res = addr.send(Ping(6)).await;
match res {
Ok(r) => {
println!("{}",r); //16
assert!(addr_2.try_send(Ping(6)).is_err())
},
_ => {}
}
}
# Arbiter
仲裁器为Actor,functions and futures 提供异步执行上下文,当Actor包含定义其Actor特定执行状态的上下文时,仲裁器将托管Actor运行的环境,最后仲裁员执行许多功能,最值得注意的是,他们能够生成新的OS线程,运行事件循环,在该事件循环上异步生成任务,并充当异步任务的助手。
# System and Arbiter
在我们之前的所有代码示例中,函数System::new
创建一个Arbiter,供您的Actor在其中运行,当您在Actor上调用start()
时,它将在系统仲裁器的线程内运行,在许多情况下,这就是使用Actix进行程序所需的全部区域。
# The event loop
一个仲裁器通过一个事件池控制一个线程,当仲裁程序生成任务时(通过Arbiter::spawn
, Context<Actor>::run_later
或类似结构体),仲裁程序会将任务排队以在该任务队列上执行。当您考虑使用仲裁器时,您可以考虑“单线程事件循环”。
一般而言,Actix确实支持并发,但是普通仲裁器(不是SyncArbiters)不支持并发。要以并发方式使用Actix,您可以使用Arbiter::new
,ArbiterBuilder
或Arbiter::start
启动多个Arbiter。
创建新的仲裁器时,这将为Actor创建一个新的执行上下文。可以使用新线程向其中添加新的Actor,但是Actor无法在仲裁器之间自由移动:它们与生成它们的仲裁器绑定在一起,但是位于不同仲裁器上的Actor仍可以使用常规的Addr / Recipient方法相互通信。传递消息的方法与Actor是在相同还是不同的仲裁程序上运行无关。
# Using Arbiter for resolving async events
如果您不是Rust Futures的专家,那么Arbiter可以是一种有用且简单的包装器,可以按顺序解决异步事件。考虑我们有两个Actor: A和B,并且我们只想在A的结果完成后才在B上运行事件,我们可以使用Arbiter::spawn
来完成此任务。
use actix::prelude::*;
struct SumActor {}
impl Actor for SumActor {
type Context = Context<Self>;
}
#[derive(Message)]
#[rtype(result = "usize")]
struct Value(usize, usize);
impl Handler<Value> for SumActor {
type Result = usize;
fn handle(&mut self, msg: Value, _ctx: &mut Context<Self>) -> Self::Result {
msg.0 + msg.1
}
}
struct DisplayActor {}
impl Actor for DisplayActor {
type Context = Context<Self>;
}
#[derive(Message)]
#[rtype(result = "()")]
struct Display(usize);
impl Handler<Display> for DisplayActor {
type Result = ();
fn handle(&mut self, msg: Display, _ctx: &mut Context<Self>) -> Self::Result {
println!("Got {:?}", msg.0);
}
}
fn main() {
let system = System::new("single-arbiter-example");
// Define an execution flow using futures
let execution = async {
// `Actor::start` spawns the `Actor` on the *current* `Arbiter`, which
// in this case is the System arbiter
let sum_addr = SumActor {}.start();
let dis_addr = DisplayActor {}.start();
// Start by sending a `Value(6, 7)` to our `SumActor`.
// `Addr::send` responds with a `Request`, which implements `Future`.
// When awaited, it will resolve to a `Result<usize, MailboxError>`.
let sum_result = sum_addr.send(Value(6, 7)).await;
match sum_result {
Ok(res) => {
// `res` is now the `usize` returned from `SumActor` as a response to `Value(6, 7)`
// Once the future is complete, send the successful response (`usize`)
// to the `DisplayActor` wrapped in a `Display
dis_addr.send(Display(res)).await;
}
Err(e) => {
eprintln!("Encountered mailbox error: {:?}", e);
}
};
};
// Spawn the future onto the current Arbiter/event loop
Arbiter::spawn(execution);
// We only want to do one computation in this example, so we
// shut down the `System` which will stop any Arbiters within
// it (including the System Arbiter), which will in turn stop
// any Actor Contexts running within those Arbiters, finally
// shutting down all Actors.
System::current().stop();
system.run();
}
# SyncArbiter
当您正常运行Actor时,使用其事件循环在系统的Arbiter线程上运行多个Actor,但是对于受CPU约束的工作负载或高度并发的工作负载,您可能希望让Actor并行运行多个实例。这就是SyncArbiter提供的功能-能够在OS线程池上启动Actor的多个实例。
请务必注意,SyncArbiter只能托管一种类型的Actor。这意味着您需要为要以这种方式运行的每种Actor类型创建一个SyncArbiter。
# Creating a Sync Actor
实施要在SyncArbiter上运行的Actor时,需要将Actor的Context从Context更改为SyncContext。
use actix::prelude::*;
struct MySyncActor;
impl Actor for MySyncActor {
type Context = SyncContext<Self>;
}
# Starting the Sync Arbiter
现在我们已经定义了一个Sync Actor,我们可以在由SyncArbiter创建的线程池上运行它。我们只能在SyncArbiter创建时控制线程数-我们以后不能添加/删除线程。
use actix::prelude::*;
struct MySyncActor;
impl Actor for MySyncActor {
type Context = SyncContext<Self>;
}
let addr = SyncArbiter::start(2, || MySyncActor);
We can communicate with the addr the same way as we have with our previous Actors that we started. We can send messages, receive futures and results, and more.Sync Actors have no Mailbox limits, but you should still use do_send
, try_send
and send
as normal to account for other possible errors or sync vs async behavior.
斐波拉且数列
use actix::prelude::*;
struct Fibonacci(pub u32);
impl Message for Fibonacci {
type Result = Result<u64, ()>;
}
struct SyncActor;
impl Actor for SyncActor {
type Context = SyncContext<Self>;
}
impl Handler<Fibonacci> for SyncActor {
type Result = Result<u64, ()>;
fn handle(&mut self, msg: Fibonacci, _: &mut Self::Context) -> Self::Result {
if msg.0 == 0 {
Err(())
} else if msg.0 == 1 {
Ok(1)
} else {
let mut i = 0;
let mut sum = 0;
let mut last = 0;
let mut curr = 1;
while i < msg.0 - 1 {
sum = last + curr;
last = curr;
curr = sum;
i += 1;
}
Ok(sum)
}
}
}
#[actix_rt::main]
async fn main() {
// start sync arbiter with 3 threads
let addr = SyncArbiter::start(5, || SyncActor);
// send 5 messages
for n in 3..32 {
println!("{:?}", addr.send(Fibonacci(n)).await.unwrap());
}
System::current().stop();
}