Akka与Actor模型:远程通信、路径、监督与测试
立即解锁
发布时间: 2025-08-19 00:05:40 阅读量: 6 订阅数: 17 


Scala 2.13编程实战与进阶
### Akka 与 Actor 模型:远程通信、路径、监督与测试
#### 1. Akka 远程通信
在实现一个完整的系统时,我们需要完成最后一部分拼图——实现 `sendBoy` 函数和 `Boy` 角色。`Boy` 不属于面包店,经理需要将 `Boy` 派到由另一个 Actor 系统表示的杂货店。为了实现这一点,我们将依赖 Akka 的位置透明性和远程功能。
##### 1.1 配置依赖和网络设置
首先,需要在 `build.sbt` 中添加远程通信的依赖:
```scala
libraryDependencies += "com.typesafe.akka" %% "akka-remote" % akkaVersion
```
然后,在 `application.conf` 中替换本地 Actor 提供者为远程提供者,并配置网络设置:
```scala
akka {
actor.provider = remote
remote {
enabled-transports = ["akka.remote.netty.tcp"]
netty.tcp {
hostname = "127.0.0.1"
port = 2552
}
}
}
```
对于代表杂货店的第二个 Actor 系统,使用相同的配置,但需要重新定义 TCP 端口:
```scala
include "application"
akka.remote.netty.tcp.port = 2553
```
##### 1.2 定义杂货店
以下是定义杂货店的代码:
```scala
import akka.actor._
import com.example.Manager.ShoppingList
import com.example.Mixer.Groceries
import com.typesafe.config.ConfigFactory
object Store extends App {
val store = ActorSystem("Store", ConfigFactory.load("grocery.conf"))
val seller = store.actorOf(Props(new Actor {
override def receive: Receive = {
case s: ShoppingList =>
ShoppingList.unapply(s).map(Groceries.tupled).foreach(sender() ! _)
}
}), "Seller")
}
```
这里不能使用默认配置,因为它已被面包店系统占用,所以需要使用 `ConfigFactory.load` 加载自定义的 `grocery.conf`。
##### 1.3 实现 sendBoy 函数
在经理中实现 `sendBoy` 函数:
```scala
private def sendBoy: ActorRef = {
val store = "akka.tcp://[email protected]:2553"
val seller = context.actorSelection(s"$store/user/Seller")
context.actorOf(Boy.props(seller))
}
```
为了让 Akka 远程部署这个 Actor,需要在 `application.conf` 中添加以下配置:
```scala
akka.actor.deployment {
/Manager/Boy {
remote = "akka.tcp://[email protected]:2553"
}
}
```
也可以在代码中直接提供部署配置:
```scala
val storeAddress = AddressFromURIString(s"$store")
val boyProps = Boy.props(seller).withDeploy(Deploy(scope = RemoteScope(storeAddress)))
context.actorOf(boyProps)
```
##### 1.4 实现 Boy 角色
```scala
object Boy {
def props(seller: ActorSelection): Props = Props(classOf[Boy], seller)
}
class Boy(seller: ActorSelection) extends Actor {
override def receive = {
case s: ShoppingList =>
seller forward s
self ! PoisonPill
}
}
```
`Boy` 接收到 `ShoppingList` 后,将消息转发给卖家,并在转发后发送 `PoisonPill` 给自己以终止自身。
##### 1.5 连接面包店的各个角色
```scala
object Bakery extends App {
val bakery = ActorSystem("Bakery")
val cook: ActorRef = bakery.actorOf(Props[Cook], "Cook")
val chef: ActorRef = bakery.actorOf(Props[Chef], "Chef")
val oven: ActorRef = bakery.actorOf(Oven.props(12), "Oven")
val baker: ActorRef = bakery.actorOf(Baker.props(oven), "Baker")
val manager: ActorRef = bakery.actorOf(Manager.props(chef, cook, baker), "Manager")
}
```
#### 2. Actor 路径
Akka Actor 是分层的,Actor 路径是通过将层次结构中每个 Actor 的名称从右到左用斜杠连接起来构建的。路径的开头是一个地址部分,用于标识 Actor 系统的协议和位置,这个地址部分称为锚点,其表示方式在本地和远程系统中有所不同。
例如,在部署配置中本地路径为 `/Manager/Boy` 的 `Boy` Actor 的完整路径为 `akka://user/Bakery/Manager/Boy`(纯本地路径),而远程 `Store` Actor 系统中 `Seller` Actor 的路径为 `akka.tcp://[email protected]:2553/user/Seller`。
Actor 路径的主要目的是定位我们要发送消息的 Actor。在技术层面上,有一个用于向 Actor 发送消息的抽象 `ActorRef`。每个 `ActorRef` 引用一个确切的 Actor,并包含一个调度器和一个邮箱。
创建 `ActorRef` 有两种方式:
1. 使用 `context.actorOf` 创建 Actor。
2. 使用 `context.actorSelection` 查找一个或多个 Actor。
提供 Actor 路径进行查找有以下几种方式:
| 方式 | 描述 |
| ---- | ---- |
| 绝对路径 | `context.actorSelection("/user/Manager/Boy")` 返回具有指定路径的单个 Actor 或空选择 |
| 相对路径 | `context.actorSelection("../sibling")` 向上到层次结构中的父级,然后向下到“兄弟”(如果存在) |
| 通配符 | `context.actorSelection("../*")` 向上到层次结构中并选择 Actor 父级的所有子级,包括当前 Actor |
#### 3. Actor 监督
在 Actor 路径中,我们看到的以 `/user` 开头的部分与 Akka 中 Actor 的创建有关。在 Akka 中,第一个 Actor 由库本身创建,称为根守护进程。实际上,
0
0
复制全文
相关推荐









