声明:本文是《Java虚拟机并发编程》的第五章,感谢华章出版社授权并发编程网站发布此文,禁止以任何形式转载此文。
截至目前我们所写的关于角色的例子中,所有角色及其客户端都运行于同一JVM进程中。但在现实生活中,有一部分开发者认为角色也应该像在Erlang中那样被用于进程间通信。而另一部分开发者则像我们在前面所演示的那样只将其应用于进程内通信。值得说明的一点是,Scala和Akka同时兼顾了这两个阵营的需求。
在Akka中,远程角色的用法与进程内角色的用法十分相似,唯一的区别就在于我们如何访问角色。Akka在底层使用了JBoss Netty和Google Protocol Buffers库来实现远程操作与本地调用的无缝衔接,使我们可以跨越进程边界,将任意角色所产生的序列化消息和引用传递给任意的远程角色。Akka提供了通过编程和配置选项两种方式来配置主机名、端口号、消息帧的大小、安全设置等配置信息。这些配置信息的详情可以参阅Akka的帮助文档,为了简单起见,在本节的示例中我们将只使用默认设置。
为了能够远程访问角色,我们需要对角色进行注册以便将角色与一个名字或ID绑定起来,从而使客户端能够借此识别所需访问的远程角色。在完成注册之后,客户端就可以通过注册ID、主机名和端口号来给角色发请求了。发送给角色的请求既可以是单向消息也可以是双向消息,形式上与和本地角色的交互基本类似。
下面让我们一起来创建一个使用远程角色的例子。过去我曾是一个系统管理员,有很多单机系统指标如可用磁盘空间、系统性能、CPU使用率等等都是系统管理员经常需要关注的。除此之外,通过在机房各处安装的传感器,我们可以通过监测室内温度、湿度等环境指标来保证高性能计算实验室可以一直正常运作。此时如果能有一个应用程序可以帮我从这些远程机器上把这些信息收集汇总起来就更好了,下面就让我们一起来写一个吧。
我们令Monitor 角色负责接收从各个远程客户端发来的系统信息。部署于各台机器上的客户端可以自行决定何时向Monitor发送这一信息。在本例中,上述客户端每次都会将系统信息分作几组数据发送给Monitor。
下面就让我们先从Monitor类开始入手。该角色就是各个客户端将要与之交互的远程角色:
<br />public class Monitor extends UntypedActor {<br />public void onReceive(Object message) {<br />System.out.println(message);<br />}<br />public static void main(final String[] args) {<br />Actors.remote().start("localhost", 8000)<br />.register("system-monitor", Actors.actorOf(Monitor.class));<br />System.out.println("Press key to stop");<br />System.console().readLine();<br />Actors.registry().shutdownAll();<br />Actors.remote().shutdown();<br />}<br />}<br />在上面的代码中,我们创建远程角色的方法与创建进程内角色别无二致——我们继承了UntypedActor并实现了其onReceive()函数。如果需要的话,我们还可以将收到的数据汇总生成为一个带图表的、展示更丰富的简报。但为了清晰起见这里我们还是先将主要注意力集中到角色的使用上面,所以这里我们只是简单地将收到的消息打印输出来。
在main()函数中,我们首先用指定的主机名和端口号启动了一个远程服务。随后我们以“system-monitor”为ID将其注册为可远程访问的角色。当需要关闭监控服务的时候,我们只需要随便敲点什么然后回车就可以了。以上就是监控服务所需的全部代码。下面在让我们看一下客户端的代码:
<br /><br />public class Client {<br />public static void main(final String[] args) {<br />ActorRef systemMonitor = remote().actorFor(<br />"system-monitor", "localhost", 8000);<br />systemMonitor.sendOneWay("Cores: " +<br />Runtime.getRuntime().availableProcessors());<br />systemMonitor.sendOneWay("Total Space: " +<br />new File("/").getTotalSpace());<br />systemMonitor.sendOneWay("Free Space: " +<br />new File("/").getFreeSpace());<br />}<br />}<br /><br />为了能够访问远程角色,客户端需要在初始化时指定远程角色的ID、主机名和端口号。一旦拿到ActorRef的引用,我们就可以将系统信息(本例中用的是字符串)通过消息发送给远程的监控服务。
下面请打开两个命令行窗口,首先在一个窗口中启动监控服务,然后在另一个窗口中启动客户端。每次运行客户端程序之后,监控服务都会把收到的新信息打印出来,具体内容如下所示:
<br /><br />Press key to stop<br />Cores: 2<br />Total Space: 499763888128<br />Free Space: 141636308992<br /><br />为了使Monitor和客户端的代码能够通过编译,我们需要引入几个额外的jar文件:akka-remote-1.1.2.jar、protobuf-java-2.3.0.jar、netty-3.2.3.Final.jar和commons-io-2.0.1.jar。
请不要被远程角色易于创建的假象所蒙蔽。在远程角色的使用场景中,我们还是需要保证所有消息都是不可变的(且可序列化的)并且角色不能更改任何共享可变状态。
原创文章,作者:Maggie-Hunter,如若转载,请注明出处:https://blog.ytso.com/140927.html