Akka 类型化:Scala 中类型化 Actors 的第一步

一则或许对你有用的小广告

欢迎加入小哈的星球 ,你将获得:专属的项目实战 / Java 学习路线 / 一对一提问 / 学习打卡/ 赠书活动

目前,正在 星球 内带小伙伴们做第一个项目:全栈前后端分离博客项目,采用技术栈 Spring Boot + Mybatis Plus + Vue 3.x + Vite 4手把手,前端 + 后端全栈开发,从 0 到 1 讲解每个功能点开发步骤,1v1 答疑,陪伴式直到项目上线,目前已更新了 204 小节,累计 32w+ 字,讲解图:1416 张,还在持续爆肝中,后续还会上新更多项目,目标是将 Java 领域典型的项目都整上,如秒杀系统、在线商城、IM 即时通讯、权限管理等等,已有 870+ 小伙伴加入,欢迎点击围观

随着几周前 Akka 2.4.0 的发布,添加了实验性 Akka Typed 模块。使用 Akka Typed 可以以类型安全的方式创建 Actors 并与之交互。因此,我们可以使用 Akka Typed 向我们的 Actor 交互添加编译时类型检查,而不是仅仅向未类型化的 Actor 发送消息。这当然是一件好事!在关于 Akka Typed 的第一篇文章中,我们将了解 Akka Typed 的几个新概念,以及如何以这种新方式创建 Actors 并与之通信。本文中使用的完整代码可以在这个 Gist 中找到。

与往常一样,让我们​​快速显示 SBT 构建文件:


 name := "akka-typed"

version := "1.0"

scalaVersion := "2.11.7"

libraryDependencies += "com.typesafe.akka" %% "akka-typed-experimental" % "2.4.0"

这将引入所需的主要 Akka 库和创建 Actors 的新 Akka Typed 方式。一旦添加,我们就可以创建我们的第一个演员。最大的区别之一是我们不再显式创建 Actors,而是定义 Behavior 并根据该行为创建 actor。为了让你更容易,Akka Typed 提供了许多帮助类来使定义 Behavior 更容易。在本文中,我们将介绍其中的一些。

简单的静态行为

首先让我们看一下简单的静态 actor 的代码。请注意,我们展示了一些额外的案例类,以使示例更有用:


 name := "akka-typed"

version := "1.0"

scalaVersion := "2.11.7"

libraryDependencies += "com.typesafe.akka" %% "akka-typed-experimental" % "2.4.0"


我们首先定义一些简单的隐含函数和一组我们将发送给我们的 Actor 的案例类。实际行为是使用静态案例类定义的。顾名思义,静态行为提供了一个始终以相同方式执行的不变的参与者。在这种情况下,我们只是打印出我们收到的消息。为了从这个行为创建一个演员,我们启动了一个新的演员系统。这个 ActorSystem 可以看作是根 actor,我们可以向它发送消息。发送消息的方式相同,这段代码的结果如下:


 name := "akka-typed"

version := "1.0"

scalaVersion := "2.11.7"

libraryDependencies += "com.typesafe.akka" %% "akka-typed-experimental" % "2.4.0"

请注意,对于 Static actor,我们不处理任何生命周期信号。我们只是处理传入的消息,并以相同的方式处理每个请求。

使用询问模式

Akka actors 的另一个重要模式是使用 ask 模式的能力。使用询问模式,我们向参与者发送消息并获得包含响应的 Future[T]。在 Akka Typed 中,您可以使用以下方法:


 name := "akka-typed"

version := "1.0"

scalaVersion := "2.11.7"

libraryDependencies += "com.typesafe.akka" %% "akka-typed-experimental" % "2.4.0"


对于这条消息,我们使用了一个额外的案例类。这个案例类不仅包含消息,还包含要响应的 actorRef。这样做的原因是因为在 Akka Typed 中我们不能直接在 actor 中访问发送者,所以我们需要在传入消息中传递它。我们的演员行为很简单。我们只是通过其 actorRef 将新消息发送回传入的 actor。这里有趣的是,请求和响应都是键入的。我们使用熟悉的“?”来询问演员的一些事情操作,并且由于我们向案例类添加了一个 replyTo 字段,所以我们传入了使用 ?功能。这 ?操作返回一个 Future[HelloMsg],在这个例子中我们只是等待它。
当我们运行这个例子时,我们得到以下信息:


 name := "akka-typed"

version := "1.0"

scalaVersion := "2.11.7"

libraryDependencies += "com.typesafe.akka" %% "akka-typed-experimental" % "2.4.0"

切换 Actor 行为

Actor 的一个很酷的事情是他们可以根据处理过的消息切换行为。这对于实现状态机、协议处理程序等非常有用。使用 Typed Actors 我们当然也可以完成此操作。我们先看看这个例子的代码:


 name := "akka-typed"

version := "1.0"

scalaVersion := "2.11.7"

libraryDependencies += "com.typesafe.akka" %% "akka-typed-experimental" % "2.4.0"


在此代码片段中,我们使用 Total[T] 案例类来实现切换行为。使用 Total 案例类,我们定义了在处理消息时需要执行的行为。我们还需要返回将在收到下一条消息时执行的新行为。在此示例中,我们切换两种不同的行为。第一个以大写字母打印所有内容,另一个以小写字母打印。所以第一条消息将以完整的小写形式打印,第二条消息以大写形式打印,依此类推。

这导致以下输出:


 name := "akka-typed"

version := "1.0"

scalaVersion := "2.11.7"

libraryDependencies += "com.typesafe.akka" %% "akka-typed-experimental" % "2.4.0"

使用完整行为

到目前为止,我们只了解了如何处理消息。除了处理消息,一些行为可能还需要响应生命周期事件。在传统方式中,这意味着覆盖特定的生命周期函数。但是,使用 Akka Typed,我们无法再访问这些功能。生命周期事件以与普通消息相同的方式传递给行为。如果您需要直接访问这些,您可以使用不同的方式来构建您的行为。其中一种选择是为此使用 Full[T] 类:


 name := "akka-typed"

version := "1.0"

scalaVersion := "2.11.7"

libraryDependencies += "com.typesafe.akka" %% "akka-typed-experimental" % "2.4.0"


如您所见,通过使用 Full[T] 类,我们可以在 MessageOrSignal 信封中获取消息或信号,我们可以按照与正常情况相同的方式对其进行处理。请注意,我们还在这个 actor 周围添加了一个装饰器。使用 ContextAware 装饰器,我们可以使 actor 上下文可用于行为(尽管我们在这里不再使用它)。

这些消息的输出如下所示:


 name := "akka-typed"

version := "1.0"

scalaVersion := "2.11.7"

libraryDependencies += "com.typesafe.akka" %% "akka-typed-experimental" % "2.4.0"

如您所见,我们收到消息或信号。

组合行为

对于本文的最后一个示例,我们将快速了解如何将行为组合在一起以创建新行为。为此,Akka Typed 引入了以下两个运算符:

- &&:将消息发送给两种行为。
- ||:首先向左侧行为发送消息,如果该行为返回未处理的响应,则尝试右侧。

在代码中,这看起来像这样:


 name := "akka-typed"

version := "1.0"

scalaVersion := "2.11.7"

libraryDependencies += "com.typesafe.akka" %% "akka-typed-experimental" % "2.4.0"


&& 系统非常简单,我们只是重用现有的行为。对于 ||我们结合了一个新的 Behavior 功能,它总是返回 Unhanded,因此应该将所有消息传递给 fullBehavior 行为。

&& 运算符的结果如下所示:


 name := "akka-typed"

version := "1.0"

scalaVersion := "2.11.7"

libraryDependencies += "com.typesafe.akka" %% "akka-typed-experimental" % "2.4.0"

如您所见,消息由两种行为处理。对于 ||运算符输出如下所示:


 name := "akka-typed"

version := "1.0"

scalaVersion := "2.11.7"

libraryDependencies += "com.typesafe.akka" %% "akka-typed-experimental" % "2.4.0"

在这里,我们可以看到在“这里无法处理!”之后。消息右侧的运算符接管。

结论

这只是对 Typed Actors 的初步了解。对我来说,到目前为止,这感觉是一种非常好的创建演员系统的方式。感觉非常直观,而且请求和响应都可以是类型这一事实很可能会导致更安全的代码。在以后的文章中,我们将回到这个主题。