- Actor
- 定义Actor
- 创建Actor
- Actor References
- 发送消息
- 示例
Actor
Actor模型是处理并发计算的概念模型。 Riker框架的核心是四个主要组件:
- ActorSystem - 每个Riker应用程序有一个管理actor生命周期的ActorSystem
- Actor - 实现Actor特征的Rust类型,以便它们可以接收消息
- Props - 每个Actor都需要一个
Props来描述如何创建一个Actor - ActorRef - 一种克隆成本低廉的轻量级类型,可用于与其底层Actor交互,例如向其发送消息让我们看看每一个,看看如何创建一个简单的应用程序。
定义Actor
Actor是计算的基本单位。 Actor只能以异步方式通过消息进行通信。 Actor可以根据收到的消息执行三个不同的操作:
- 将有限数量的消息发送给其他Actor参与者
- 创造有限数量的新Actor
- 更改其状态或指定用于接收的下一条消息的行为
Actor参与者通过传递消息来相互交互。 对上述行为没有假设的顺序,它们可以同时进行。 并发发送的两条消息可以按任意顺序到达。
要定义一个actor,系统需要理解一个actor应该如何处理它收到的消息。 要做到这一点,只需在数据类型上实现Actor特征,并至少提供一个接收方法。
Rust代码:
struct MyActor;impl Actor for MyActor {type Msg = String;fn receive(&mut self,ctx: &Context<Self::Msg>,msg: Self::Msg,sender: Option<ActorRef<Self::Msg>>) {println!("received {}", msg);}}
在此示例中,一个简单的struct MyActor实现了Actor特征。 当消息发送到MyActor时,系统会将其调度为立即执行。 调用receive函数并将消息打印到stdout。
创建Actor
每个应用程序都有一个ActorSystem。 actor系统提供actor管理和运行时在发送消息时执行actor。 它还提供基本服务,如启动actor和暴露系统服务。
启动actor系统:
let model: DefaultModel<String> = DefaultModel::new();let sys = ActorSystem::new(&model).unwrap();
在这里,我们看到actor是使用ActorSystem :: new启动的。 但是这个型号是什么? 该模型允许我们配置应用程序中使用的消息类型,并配置用于核心服务的模块。 我们将在本文档后面详细介绍该模型。
一旦我们启动了Actor系统,我们就准备启动一些Actor了。
启动一个Actor:
let props = Props::new(Box::new(MyActor::new));let my_actor = sys.actor_of(props, "my-actor");
每个actor都需要一个包含actor的工厂方法的Props,在本例中是MyActor :: new,以及该方法所需的任何参数。 然后将Props与actor_of一起使用以创建actor的实例。 还需要一个名称,以便我们可以在以后查找,如果需要的话。
虽然这只是两行代码,但很多事情都在幕后发生。 Actor生命周期和状态由系统管理。 当一个actor开始时它会保留这些属性,以防它再次需要它来重启actor,如果它失败了。 当创建一个actor时,它会获得自己的邮箱来接收消息,并且其他感兴趣的actor会被告知有关加入系统的新actor。
Actor References
当使用actor_of启动actor时,系统返回对actor的引用,即ActorRef。 实际的actor实例仍然无法访问,其生命周期由系统管理和保护。 在Rust术语中,系统具有并始终维护actor实例的“所有权”。 当你与Actor互动时,你实际上是与Actor的ActorRef进行互动! 这是Actor模型的核心概念。
ActorRef始终引用actor的特定实例。 当同一个Actor的两个实例启动时,它们仍然被认为是单独的actor,每个actor都有不同的ActorRef。
注意 : ActorRef很轻量,可以克隆(它们实现Clone)而不必过多关注资源。 引用也可以在
Props中用作另一个actor的工厂方法中的一个字段,一种称为天赋的模式。 ActorRef也是Send,因此它可以作为消息发送给另一个actor。
发送消息
Actor只能通过消息进行通信。 他们是孤立的。 他们永远不会暴露他们的状态或行为。
如果我们想向actor发送消息,我们在actor的ActorRef上使用tell方法:
let my_actor = sys.actor_of(props, "my-actor");my_actor.tell("Hello my actor!".into(), None);
这里我们向MyActor actor发送了一个String类型的消息。 第二个参数让我们将发件人指定为Option <ActorRef>。 由于我们从主节点发送消息而不是从Actor的接收节点发送消息,因此我们将发送方设置为无。
Riker在处理消息时提供某些保证:
- 消息传递是“最多一次”: 消息将传递失败或传递一次, 没有重复传递相同的消息。
Actor随时处理一条消息- 消息存储在actor的邮箱中,以便接收它们
示例
让我们回到我们的MyActor,并将我们目前所见的内容与一个完整的例子结合起来:
Cargo.toml依赖项:
[dependencies]riker = "0.2.0"riker-default = "0.2.0"
extern crate riker;extern crate riker_default;#[macro_use]extern crate log;use std::time::Duration;use riker::actors::*;use riker_default::DefaultModel;struct MyActor;// implement the Actor traitimpl Actor for MyActor {type Msg = String;fn receive(&mut self,_ctx: &Context<Self::Msg>,msg: Self::Msg,_sender: Option<ActorRef<Self::Msg>>) {debug!("Received: {}", msg);}}// provide factory and props functionsimpl MyActor {fn actor() -> BoxActor<String> {Box::new(MyActor)}fn props() -> BoxActorProd<String> {Props::new(Box::new(MyActor::actor))}}// start the system and create an actorfn main() {let model: DefaultModel<String> = DefaultModel::new();let sys = ActorSystem::new(&model).unwrap();let props = MyActor::props();let my_actor = sys.actor_of(props, "my-actor").unwrap();my_actor.tell("Hello my actor!".to_string(), None);std::thread::sleep(Duration::from_millis(500));}
在这里,我们启动了actor系统和MyActor的一个实例。 最后,我们向Actor发送了一条消息。 你还会注意到我们还提供了一个工厂函数actor()和Props函数props()作为MyActor实现的一部分。
要查看此示例项目,请单击此处。
注意:如果actor的工厂方法需要参数,则可以使用Props :: new_args。 有关示例,请参阅Rustdocs。
在此页面上,您学习了使用actor创建Riker应用程序的基础知识。 让我们继续下一节,看看如何使用自己的消息类型:
