讨喜的隔离可变性(十二)基于角色模型的局限性和小结

声明:本文是《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。

请不要被远程角色易于创建的假象所蒙蔽。在远程角色的使用场景中,我们还是需要保证所有消息都是不可变的(且可序列化的)并且角色不能更改任何共享可变状态。

文章转自 并发编程网-ifeve.com

时间: 2024-08-03 15:56:29

讨喜的隔离可变性(十二)基于角色模型的局限性和小结的相关文章

讨喜的隔离可变性(十)使用Transactor

声明:本文是<Java虚拟机并发编程>的第五章,感谢华章出版社授权并发编程网站发布此文,禁止以任何形式转载此文. Akka transactor或事务角色为我们提供了一种将多个角色的执行过程合并到一个事务中的方法.顾名思义,transactor可以将多个角色对于托管STM Ref对象的更改变成原子操作,即仅当外围事务提交成功之后,对于那些托管对象的变更才能生效,否则所有的变更都会被丢弃. Transactor提供了三种处理消息的方法: 默认情况下,Transactor会在其自己的事务中处理消息

讨喜的隔离可变性-前言

曾有个的医嘱是这样说的:"如果它伤到了你,那就别再用它了".在并发编程领域,共享可变性就是那个"它". 虽然JDK的线程API使我们可以非常容易地创建线程,但如何防止线程冲突和逻辑混乱却又成了大问题.STM虽然可以解决部分问题,但是在一些类似Java这样的语言中,我们仍不得不非常小心谨慎地避免非托管可变变量和事务逻辑中产生某些副作用.而令人惊讶的是,当共享可变性消失的时候,所有那些令人纠结的问题也都随之消失了. 事实证明,在相同数据集上起多个线程互相冲突地执行是行不

讨喜的隔离可变性(一)用角色实现隔离可变性

Java将OOP变成了可变性驱动(mutability-driven)的开发模式[1],而函数式编程则着重强调不可变性,而这两种极端的方式其实都是有问题的.如果每样事物都是可变的,那么我们就需要妥善处理可见性和竞争条件.而在一个真实的应用程序中,也并非所有事物都是不可变的.即使是纯函数式语言也提供了代码限制区,在该区域内允许出现带副作用的逻辑以及按顺序执行这些逻辑的方法.但无论我们倾向于哪种编程模型,避免共享可变性都是毋庸置疑的. 共享可变性--并发问题的根源所在--是指多个线程可以同时更改相同

讨喜的隔离可变性(六)多角色协作

在使用基于角色的编程模型时,只有当多个角色互相协作.同心协力解决问题时,我们才能真正从中获益并感受到其中的乐趣.为了更好地利用并发的威力,我们通常需要把问题拆分成若干个子问题.不同的角色可以负责不同的子问题,而我们则需要对角色之间的通信进行协调.下面我们将通过重写计算目录大小的例子来学习如何在进行多角色协作. 在4.2节中,我们写了一个计算给定目录下所有文件大小的程序.在那个例子中,我们启动了100个线程,每个线程都负责扫描不同的子目录,并在最后异步地将所有计算结果累加在一起.而本节中我们将看到

讨喜的隔离可变性(十三)角色的特性

基于角色的并发模型降低了隔离可变性编程的难度,但该模型在适用场景上还是存在一些限制. 由于角色是通过消息来进行彼此间通信的,所以在那些没有强制不可变性的语言中,我们就必须人工来保证消息都是不可变的.传递可变消息将导致线程安全问题并最终使整个应用陷入共享可变性的险境当中,所以当手头的辅助工具还没有发展到可以帮助我们自动查验消息的不可变性之前,保证消息不可变性的重担暂时还是得由我们程序员来肩负. 角色都是各自异步运行的,彼此之前可以通过传递消息来进行协作.但某些角色的意外失败有可能导致其他角色饿死-

讨喜的隔离可变性(五)同时使用多个角色

声明:本文是<Java虚拟机并发编程>的第五章,感谢华章出版社授权并发编程网站发布此文,禁止以任何形式转载此文. 在使用基于角色的编程模型时,只有当多个角色互相协作.同心协力解决问题时,我们才能真正从中获益并感受到其中的乐趣.为了更好地利用并发的威力,我们通常需要把问题拆分成若干个子问题.不同的角色可以负责不同的子问题,而我们则需要对角色之间的通信进行协调.下面我们将通过重写计算目录大小的例子来学习如何在进行多角色协作. 在4.2节中,我们写了一个计算给定目录下所有文件大小的程序.在那个例子中

讨喜的隔离可变性(二)角色的特性

角色是一种能够接收消息.处理请求以及发送响应的自由运行的活动(activity),主要被设计用来支持异步化且高效的消息传递机制. 每个角色都有一个内建的消息队列,该队列与手机上所使用的短信队列十分相似.假设Sally和Sean同时给Bob的手机发了短信,则运营商将会把这两条短信都保存起来以便Bob在方便的时候取走.类似地,基于角色的并发库允许多个角色并发地发送消息.默认情况下,消息发送者都是非阻塞的:它们会先把消息发送出去,然后再继续处理自己的业务逻辑.类库一般会让特定的角色顺序地拾取并处理消息

讨喜的隔离可变性(八)类型化角色和Murmurs

使用了类型化角色的EnergySource使我们能够以调用函数的形式来掩盖后台顺序处理异步消息的过程,在实现了线程安全的同时又可以免去显式同步的困扰.虽然创建类型化角色并不困难,但此时我们的EnergySource却还是一个丢失了关键特性的半成品--即还没有可以周期性自动补充电量的能力. 在上一章我们所实现的版本中,由于整个动作都是在后台完成,所以电量补充的动作是不需要任何用户介入的.只要我们启动了电源,就会有一个专门的timer负责每秒钟为电源增加一格电量. 然而在使用了类型化角色的版本中,实

讨喜的隔离可变性(三)创建角色

正如前面曾经提到过的那样,虽然我们有很多支持角色的类库可供选择,但是在本书中我们将使用Akka.这是一个基于Scala的类库,该类库拥有非常好的性能和可扩展性.并同时支持角色和STM.此外,该类库还可以被用于多种JVM上的语言中.在本章中,我们将注意力集中在Java和Scala身上.而在下一章,我们将会学习如何在其他语言中使用Akka的角色.   图 8‑2 某个角色的生存周期 由于Akka是用Scala实现的,所以在Scala中创建和使用角色非常简单并且更加自然,从Akka API的实现里我们