60个版本

0.13.5 2024年6月9日
0.13.3 2024年2月7日
0.13.1 2023年8月30日
0.13.0 2022年3月1日
0.3.3 2017年11月21日

#82 in 异步

Download history 55639/week @ 2024-05-04 62931/week @ 2024-05-11 78664/week @ 2024-05-18 59545/week @ 2024-05-25 73886/week @ 2024-06-01 81102/week @ 2024-06-08 65542/week @ 2024-06-15 66529/week @ 2024-06-22 58430/week @ 2024-06-29 64003/week @ 2024-07-06 58395/week @ 2024-07-13 66757/week @ 2024-07-20 64528/week @ 2024-07-27 68215/week @ 2024-08-03 85277/week @ 2024-08-10 81715/week @ 2024-08-17

312,504 每月下载量
用于 少于 277 crates

MIT/Apache

235KB
5K SLoC

Actix

Rust的actor框架

crates.io Documentation Minimum Supported Rust Version License Dependency Status
CI codecov Downloads Chat on Discord

文档

特性

  • 异步和同步actor
  • 本地/线程上下文中的actor通信
  • 使用 futures 处理异步消息
  • Actor监督
  • 类型化消息(无 Any 类型)
  • 在稳定的Rust 1.68+上运行

用法

要使用 actix,将以下内容添加到您的 Cargo.toml

[dependencies]
actix = "0.13"

初始化Actix

为了使用actix,您首先需要创建一个 System

fn main() {
    let system = actix::System::new();

    system.run();
}

Actix使用 Tokio 运行时。 System::new() 创建一个新的事件循环。 System.run() 启动Tokio事件循环,一旦 System actor收到 SystemExit 消息,则结束。

实现Actor

为了定义actor,您需要定义一个结构体并使其实现 Actor trait。

use actix::{Actor, 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();

    let _addr = system.block_on(async { MyActor.start() });

    system.run().unwrap();
}

启动新actor是通过 Actor traitstartcreate 方法实现的。它提供了多种创建actor的方式;有关详细信息,请参阅文档。您可以实现Actor trait的 startedstoppingstopped 方法。 started 在actor启动时被调用,而 stopping 在actor完成时被调用。有关actor生命周期的更多信息,请参阅API文档。

处理消息

演员通过发送消息与其他演员进行通信。在actix中,所有消息都是有类型的。让我们定义一个简单的带有两个 usize 参数和一个将接受此消息并返回两个数字之和的演员的 Sum 消息。在这里,我们使用 #[actix::main] 属性作为启动我们的 System 和驱动主函数的更简单方式,这样我们就可以轻松地 .await 来自 Actor 的响应。

use actix::prelude::*;

// this is our Message
// we have to define the response type (rtype)
#[derive(Message)]
#[rtype(usize)]
struct Sum(usize, usize);

// Actor definition
struct Calculator;

impl Actor for Calculator {
    type Context = Context<Self>;
}

// now we need to implement `Handler` on `Calculator` for the `Sum` message.
impl Handler<Sum> for Calculator {
    type Result = usize; // <- Message response type

    fn handle(&mut self, msg: Sum, _ctx: &mut Context<Self>) -> Self::Result {
        msg.0 + msg.1
    }
}

#[actix::main] // <- starts the system and block until future resolves
async fn main() {
    let addr = Calculator.start();
    let res = addr.send(Sum(10, 5)).await; // <- send message and get future for result

    match res {
        Ok(result) => println!("SUM: {}", result),
        _ => println!("Communication to the actor has failed"),
    }
}

所有与演员的通信都通过一个 Addr 对象进行。你可以发送一个消息而不等待响应,或者你可以发送一个特定消息给演员。 Message 特性定义了消息的结果类型。

演员状态和特定消息的订阅

你可能已经注意到,ActorHandler 特性的方法接受 &mut self,因此你可以在演员中存储任何东西,并在必要时对其进行修改。

地址对象需要一个演员类型,但如果我们只想向一个可以处理该消息的演员发送特定消息,我们可以使用 Recipient 接口。让我们创建一个新的使用 Recipient 的演员。

use actix::prelude::*;
use std::time::Duration;

#[derive(Message)]
#[rtype(result = "()")]
struct Ping {
    pub id: usize,
}

// Actor definition
struct Game {
    counter: usize,
    name: String,
    recipient: 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!("[{0}] Ping received {1}", self.name, msg.id);

            // wait 100 nanoseconds
            ctx.run_later(Duration::new(0, 100), move |act, _| {
                act.recipient.do_send(Ping { id: msg.id + 1 });
            });
        }
    }
}

fn main() {
    let system = System::new();

    system.block_on(async {
        // To create a cyclic game link, we need to use a different constructor
        // method to get access to its recipient before it starts.
        let _game = 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,
                name: String::from("Game 2"),
                recipient: addr.recipient(),
            }
            .start();

            // let's start pings
            addr2.do_send(Ping { id: 10 });

            // now we can finally create first actor
            Game {
                counter: 0,
                name: String::from("Game 1"),
                recipient: addr2.recipient(),
            }
        });
    });

    // let the actors all run until they've shut themselves down
    system.run().unwrap();
}

聊天示例

查看这个 聊天示例,它展示了在客户端/服务器网络服务中更全面的用法。

贡献

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

许可证

本项目受以下任一许可证的许可:

任选其一。

行为准则

向 actix 存储库的贡献是在贡献者协约的条款下组织的。Actix 团队承诺将介入以维护该行为准则。

依赖项

~4–13MB
~135K SLoC