7.2 Akka 之 Hello World
在了解了Actor的基本行为模式后,我们通过简单的Hello World程序来进一步了解一下Akka的开发。
首先让我们看一下,第1个Actor的实现:
01 public class Greeter extends UntypedActor { 02 public static enum Msg { 03 GREET, DONE; 04 } 05 06 @Override 07 public void onReceive(Object msg) { 08 if (msg == Msg.GREET) { 09 System.out.println("Hello World!"); 10 getSender().tell(Msg.DONE, getSelf()); 11 } else 12 unhandled(msg); 13 } 14 }
上述代码中,定义了一个欢迎者(Greeter)Actor,它继承自UntypedActor(它自然就是Akka中的核心成员了)。UntypedActor就是我们所说的Actor,之所以这里强调是无类型的,那是因为在Akka中,还支持一种有类型的Actor。有类型的Actor可以使用系统中的其他类型构造,可以缓解Java单继承的问题。因为你在继承了UntypedActor后,就不能再继承系统中的其他类了。如果你一定想这么做,那么就只能选择有类型的Actor。否则,UntypedActor应该就是你的首选。
在这里,代码第2~4行,定义了消息类型。这里只有两种类型,欢迎(GREET)以及完成(DONE)。当Greeter收到GREET消息时,就会在控制台打印“Hello World”,并且向消息发送方发送DONE信息(第10行)。
与Greeter交流的另外一个Actor是HelloWorld,它的实现如下:
01 public class HelloWorld extends UntypedActor { 02 ActorRef greeter; 03 04 @Override 05 public void preStart() { 06 greeter = getContext().actorOf(Props.create(Greeter.class), "greeter"); 07 System.out.println("Greeter Actor Path:" + greeter.path()); 08 greeter.tell(Greeter.Msg.GREET, getSelf()); 09 } 10 11 @Override 12 public void onReceive(Object msg) { 13 if (msg == Greeter.Msg.DONE) { 14 greeter.tell(Greeter.Msg.GREET, getSelf()); 15 getContext().stop(getSelf()); 16 } else 17 unhandled(msg); 18 } 19 }
上述代码实现了一个名为HelloWorld的Actor。第5行的preStart()方法为Akka的回调方法,在Actor启动前,会被Akka框架调用,完成一些初始化的工作。在这里,我们在HelloWorld中创建了Greeter的实例(第6行),并且向它发送GREET消息(第8行)。此时,由于创建Greeter时使用的是HelloWorld的上下文,因此,它属于HelloWorld的子Actor。
第12行定义的onReceive()函数为HelloWorld的消息处理函数。在这里,只处理DONE的消息。在收到DONE消息后,它会再向Greeter发送GREET消息,接着将自己停止。
因此,Greeter会前后收到两条GREET消息,会打印两次“Hello World”。
最后,让我们看一下主函数main():
1 public class HelloMainSimple { 2 public static void main(String[] args) { 3 ActorSystem system = ActorSystem.create("Hello",ConfigFactory.load("samplehello.conf")); 4 ActorRef a = system.actorOf(Props.create(HelloWorld.class),"helloWorld"); 5 System.out.println("HelloWorld Actor Path:" + a.path()); 6 } 7 }
程序第3行,创建了ActorSystem,表示管理和维护Actor的系统。一般来说,一个应用程序只需要一个ActorSystem就够用了。ActorSystem.create()的第1个参数“Hello”为系统名称,第2个参数为配置文件。
第4行通过ActorSystem创建一个顶级的Actor(HelloWorld)。
配置文件samplehello.conf的内容如下:
akka { loglevel = INFO }
在这里,只是简单地配置了一下日志级别为INFO。
执行上述代码,可以看到以下输出:
1 HelloWorld Actor Path:akka://Hello/user/helloWorld 2 Greeter Actor Path:akka://Hello/user/helloWorld/greeter 3 Hello World! 4 Hello World! 5 [INFO] [05/13/2015 21:15:01.299] [Hello-akka.actor.default-dispatcher-2] [akka://Hello/user/helloWorld] Message [geym.akka.demo.hello.Greeter$Msg] from Actor[akka://Hello/user/helloWorld/greeter#-1698722495] to Actor[akka://Hello/user/helloWorld#-1915075849] was not delivered. [1] dead letters encountered. This logging can be turned off or adjusted with configuration settings 'akka.log-dead-letters' and 'akka.log-dead-letters-during-shutdown'.
第1行打印了HelloWorld Actor的路径。它是系统内第1个被创建的Actor。它的路径为:akka://Hello/user/helloWorld。其中第1个Hello表示ActorSystem的系统名,可以看一下我们构造这ActorSystem时,传入的第1个参数就是Hello。接着user表示用户Actor。所有的用户Actor都会挂载在user这个路径下。第3个helloWorld就是这个Actor的名字。
同理,第2个Greeter Actor的路径结构和HelloWorld是完全一致的。输出的第3、4行显示了Greeter打印的两条信息。第5行表示系统遇到了一条消息投递失败,失败的原因是HelloWorld将自己终止了,导致Greeter发送的信息无法投递。
可以看到,当使用Actor进行并行程序开发时,我们的关注点已经不在线程上了。实际上,线程调度已经被Akka框架进行封装,我们只需要关注Actor对象即可。而Actor对象之间的交流和普通的对象的函数调用有明显区别。它们是通过显示的消息发送来传递信息的。
当系统内有多个Actor存在时,Akka会自动在线程池中选择线程来执行我们的Actor。因此,多个不同的Actor有可能会被同一个线程执行,同时,一个Actor也有可能被不同的线程执行。因此,一个值得注意的地方是:不要在一个Actor中执行耗时的代码,这样可能会导致其他Actor的调度出现问题。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论