Akka 使用系列之一: 快速入门

最近在看 Spark 相关的资料,准备整理一个 Spark 系列。Akka 是 Spark 实现内部通讯的组件,Spark 启动过程的第一步便是建立 Akka 的 ActorSystem。因此看了有几篇文章学习了下 Akka 相关知识。

1 Actor 模型和 Akka 简介

Actor 模型由 Carl Hewitt 于上世纪70年代早期提出,目的是为了解决分布式编程中一系列的编程问题。Actor 的要点包括:Actor 是一个个相互之间独立的实体; Actor 可以通过消息来通信,一个 Actor 收到其他Actor的信息后,可以根据需要作出各种相应反应;消息的类型可以是任意的,消息的内容也可以是任意的;当一个 Actor 收到多个消息时,它先建立一个消息队列,将接收到的消息就放入队列,每次从队列中取出一个消息体进行处理。

Akka 是一个用 Scala 编写的库,用于简化编写容错的、高可伸缩性的 Actor 模型应用。Akka 使得开发人员可以更轻松地开发具有容错性、可扩展性和跨平台的并发程序,在工业界得到了广泛应用。

2 Akka 入门

下面我们将从一个学生老师例子出发,快速入门 Actor 模型。例子是从博客 rerun 拿来的 (我就是看这个博客学习 Akka 的,强烈推荐英语好的同学看看)。这个例子有两个角色,一个是勤学好问的学生,一个是睿智的老师。每天早上,学生都会给老师发送一封邮件,向老师请教问题;而老师看到邮件之后,通过邮件给学生发送答案。在这个过程中,有几点需要注意:

1. 邮件一旦发送,就不能改变; 2. 学生和老师都按照自己的工作节奏检查邮箱; 3. 学生发送邮件之后,可以不等老师的回复。即工作流程可以是阻塞,也可以是非阻塞;

Akka 用于简化编写容错的、高可伸缩性的 Actor 模型应用,即我们很容易用 Akka 实现上述学生老师的 Actor 模型。我们先建立老师 Actor, 将老师 Actor 实现成一个服务(我是不是想歪了),代码如下所示。

class TeacherActor extends Actor {
  def receive = {
    case "1+1等于多少?"           => {
      val originalSender = sender;//
      sender ! "1+1等于2"
    }
    case "历史上规模最大的众筹行动是什么?" => {
      val originalSender = sender;//
      sender ! "历史上规模最大的众筹行动是 +1s"
    }
    case "腾讯第一网红是谁?"     => {
      val originalSender = sender;//
      sender ! "腾讯第一网红是\"我去\""
    }
    case _              => {
      val originalSender = sender;//
      sender ! "这个问题,老师也得查查书"
    }
  }
}

object TeacherServices extends App { 
  
  val config = ConfigFactory
              .parseResources("lietal.conf")
              .getConfig("RemoteServerSideActor")
  //读入配置
  val system = ActorSystem("TeacherService",  config)
  //使用配置,建立 Actor 模型系统 ActorSystem。
  //ActorSystem 是访问 Actor 模型系统的接口,类似于 Spark 的 SparkContext。

  system.actorOf(Props[TeacherActor], "teacherActor") 
  //创建TearcherActor,返回一个引用
  //teacherActor 是 Actor 的名,客户端需要用
}

然后我们建立学生 Actor, 将学生 Actor 作为客户端。

class StudentActor extends Actor{
  val path = "akka.tcp://remote-system@127.0.0.1:4999/"
              +"user/teacherActor" 
  // 远程Actor的路径,通过该路径能够获取到远程Actor的一个引用
  val remoteServerRef = context.actorSelection(path) 
  // 获取到远程Actor的一个引用,通过该引用可以向远程Actor发送消息

  def receive = {
      case res:String => {
        println (res)
        //打印出从老师 Actor 获得的答案
      }
      case time:Long => {
        remoteServerRef ! "历史上规模最大的众筹行动是什么?";
      }
    }
}

object StudentSimulator extends App{
  val config = ConfigFactory
               .parseResources("lietal.conf")
               .getConfig("RemoteClientSideActor")
  //读入客户端配置
  val actorSystem = ActorSystem("StudentClient",  config);
  //使用配置,建立 ActorSystem
  val studentActor = actorSystem.actorOf(Props[StudentActor])
  //获得 StudentActor 的一个引用。
  //在程序中 Actor 不能直接被访问。
  //所有操作都必须通过 ActorRef 引用。
  
  while(true){
      studentActor ! 7.toLong    // 7 点起床。。
      Thread.sleep(5000) // 假装一天过去了
  }
}

不管是服务端还是客户端,程序开始都从 lietal.conf 配置文件读入相应的配置。其中服务端读入 RemoteServerSideActor 的配置,而客户端读入 RemoteClientSideActor 的配置。lietal.conf 配置文件放在资源目录 src/main/resources,以便打包时打入包内。lietal.conf 的内容如下所示。

RemoteServerSideActor {
  akka {
    actor {
      provider = "akka.remote.RemoteActorRefProvider"
    }
    remote {
      enabled-transports = ["akka.remote.netty.tcp"]
      netty.tcp {
        hostname = "127.0.0.1"
        port = 4999
      }
    }
  }
}

RemoteClientSideActor {
  akka {
    actor {
      provider = "akka.remote.RemoteActorRefProvider"
    }
    remote {
      enabled-transports = ["akka.remote.netty.tcp"]
      netty.tcp {
        hostname = "127.0.0.1"
        port = 5000
      }
    }
  }
}

从配置文件来看,如果我们在服务端启动两个老师 Actor, 他们会共用一个端口。我们很容易理解,所有发往老师 Actor 的消息都发往了服务器的一个端口,Akka 内部有一套机制将消息分发到不同的 Actor 中。这套机制就是 Akka 的 dispatcher,负责分发不同的消息到不同的 Actor。

完整项目代码已经上传到 Github 上了,需要的同学自取。将完整项目打包之后,分别以 TearcherService 和 StudentClient 为主类运行程序,老师 Actor 和学生 Actor 之间的通信就运行起来了。下面是老师服务端运行的结果。

下面是学生客户端运行的结果。

3 总结

一开始我只想实现一个单机版本的老师学生 Actor,实现之后发现不能体现 Akka 的特点,因此又实现一个网络版的老师学生 Actor。实现完网络版之后,稍微加深了对 Actor 之间消息传递的理解。完整项目代码已经上传到 Github 上啦。

本文主要参考了 http://shiyanjun.cn/archives/1178.html 和 http://rerun.me/2014/09/11/introducing-actors-akka-notes-part-1/ 两篇博客。第一篇博客注重实战,代码比较完整;第二篇则深入浅出的解释了相关的概念原理。推荐大家阅读。

Akka 系列系列文章

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏程序员宝库

我的编程之路:知识管理与知识体系

本文的资料放到了Github Repo(https://github.com/wxyyxc1992/Coder-Knowledge-Graph)(本文介绍的这种...

3755
来自专栏北京马哥教育

据说这篇总结覆盖了一般Python开发面试中可能会问到的大部分问题

通信背景,工作一年多不到两年。之前一直在做C++的MFC软件界面开发工作。公司为某不景气的国企研究所。(喏,我的工作经验很水:1是方向不对;2是行业有偏差)。然...

1392
来自专栏PPV课数据科学社区

[工具]7个应知的Python库

在我多年的Python编程生涯中,以及在GitHub上探索漫游,我碰到了一些库,用起来特别愉快,这篇文章,就是来扩散这方面的知识。我决定排除很优秀的几个库,像...

3056
来自专栏顶级程序员

Java 9、10、11,谁才是Java程序员的本命?

之前,我们在《Java 10无跳票发布,主推的新特性引争议》的文章中做了一个小的调查,主要是调查现在的Java程序员都在使用哪个版本的Java?根据调查结果,绝...

973
来自专栏编程

记一次非常愉悦的 Python 使用经历

临近毕业,班群里发了个要核对的信息的表格,要我们检查一下,涉及到毕业证。 于是我就下载来看一看,结果傻了眼:(敏感信息已打码) ? 是的,姓名和身份证号都是明文...

2265
来自专栏牛客网

网易云音乐Java面经(共三面)

【每日一语】很多人都无从得知自己的天赋,因为找不到相信他们的老师。于是他们深信自己很笨。——《心灵捕手》

6931
来自专栏Java Web

线程和进程基础——翻译文

前言 所有的内容均来自:http://www.qnx.com/developers/docs/6.4.1/neutrino/getting_started/s...

3115
来自专栏企鹅号快讯

编程语言中间令人无语的规则

我们都知道,软件开发人员每天都在做各种各样的决策:如何更好地实现功能、如何修复bug、如何改进应用程序性能等等。但是他们也在其他人的工作成果中继续自己的决定,例...

3695
来自专栏Crossin的编程教室

【每周一坑】特殊的生日

好吧,我在跳票的道路上又双叒叕前进了一步……今天终于厚着脸皮来更新【每“周”一坑】啦。感谢在后台孜孜不倦催促我的同学们。 在出题前,先插两句: 上次关于 Pyt...

35211
来自专栏SDNLAB

【一课专栏】解构ODL引子 - ODL入坑之路·上篇

做为一名无证驾驶ODL这辆SDN战车3年多的老司机,在基于ODL进行商用SDN控制器的研发过程中,总结了一些经验和教训,也有一些心得体会,借这个机会与大家一起交...

4872

扫码关注云+社区

领取腾讯云代金券