前面说过,对于 Actor 模型个人认为最简单的理解方式就是消息中间件。Actor 的本质是事件驱动,即接收消息并处理。反映到编程上,Actor 的开发也类似于消息中间件 consumer 的开发,无非是换了个接口、多几个功能罢了。
话不多说,看代码:
public class FriendActor extends AbstractActor { @Override public Receive createReceive() { return receiveBuilder() .match(Ping.class, this::onReceivePing) .matchAny(obj -> log.warn("unknown request: {}.", obj)) .build(); } private void onReceivePing(Ping ping) { getSender().tell(AskResponse.succeed(null), getSelf()); } }首先自然是新建类并实现接口 AbstractActor,该接口需要重写 createReceive 方法,该方法需要一个 Receive 对象作为返回值。对于开发者而言,需要做的就是构建这个 Receive 对象,也就是指明该 Actor 接受到什么类型的消息时进行什么样的处理。
3.2 初始化 ActorSystemActor 作为处理消息的“角色”,就像工厂中的一个个工人,每个人各司其职,兢兢业业地接收指令完成任务。然而群龙不能无首,就像现实生活中工人需要由工厂来组织管理一样,Actor 也需要自己的工厂—— ActorSystem。为此,创建 Actor 之前,首先需要创建 ActorSystem。
PowerJob 使用以下方法创建 ActorSystem。其中,第一个参数指明了该 ActorSystem 的名称,第二个参数则传入了该 ActorSystem 所使用的配置信息,包括工作端口、序列化方式、日志级别等。
actorSystem = ActorSystem.create("powerjob-server", akkaConfig);完成 ActorSystem 这个“工厂”的创建后,就可以正式开始创建 Actor 了,代码如下所示:
actorSystem.actorOf(Props.create(FriendActor.class), "friend_actor");其中,第一个参数Props是一个用来在创建 Actor 时指定选项的配置类;
第二个参数则指定了该 Actor 的名称,通过该 Actor 的名称和其 ActorSystem 的名称,我们就可以构建出路径 akka://powerjob-server/user/server_actor(本地路径,远程路径需要变更协议并添加地址),然后轻松得根据该路径找到该 Actor,实现通信。
3.3 信息交互完成 ActorSystem 的初始化和 Actor 的创建后,就可以正式使用 Akka 框架了。PowerJob 主要使用 Akka 框架的 remote 组件,用于完成系统中各个分布式节点的通讯。
String actorPath = "akka://powerjob-server@192.168.1.1/user/friend_actor"; ActorSelection actorSelect = actorSystem.actorSelection(actorPath); actorSelect.tell(startTaskReq, null);和其他通讯方式一样,进行通讯前,需要首先获取目标地址。根据 akka-remote 的语法规范,指定目标 Actor 的名称、其所在的 ActorSystem 名称和目标机器地址,即可获取用于通讯的 URI。得到 URI 后,便可通过 actorselection() 方法获取 Actorselection 对象。通过 Actorselection 对象,调用 tell 方法就可以向目标 Actor 发送消息了。
那么细心的小伙伴肯定要问了,PowerJob 之所以采用 akka-remote 作为底层通讯框架,是看上了它极简的通讯 API,看到这里,也没发现有多简单啊。发送一个 HTTP 请求,用高层封装库其实也就差不多三行代码的样子,你这用个 Akka 前置准备工作还那么多,说好的简单呢?那么下面就带大家来一探究竟,akka-remote 到底简单在哪里~
首先,如果不选择现有的协议,自己用 Netty 造轮子,那光 server、client、listener、codec 就一大堆代码了。如果使用现有协议如 HTTP,发送也许 3 行代码能搞定,但接收一定远不止三行。HTTP 全称超文本传输协议,那么传输的自然已经是经过序列化的文本数据了,所以接收方需要自行进行解码、解析,更别提异常处理、失败重试等功能了。而 akka-remote 呢?从刚刚 Actor 的代码中可以看出,match 方法后面跟的是一个具体的类,也就是说 Akka 自动帮你完成了反序列化,作为消息的接收方,是真正的拿到就能用,没有任何多余代码。同时,Akka 已经帮你搞定了各种异常后的处理。也就是说,使用 akka-remote,可以让数据接收方非常的简单,只专注逻辑的实现。