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

UXGFos 5年前
   <p>Akka 是 Spark 实现内部通讯的组件,Spark 启动过程的第一步便是建立 Akka 的 ActorSystem。因此看了有几篇文章学习了下 Akka 相关知识。</p>    <p><img src="https://simg.open-open.com/show/0d63509940e15c7236e15f37daacf4da.png"></p>    <h3>1 Actor 模型和 Akka 简介</h3>    <p>Actor 模型由 Carl Hewitt 于上世纪70年代早期提出,目的是为了解决分布式编程中一系列的编程问题。Actor 的要点包括:Actor 是一个个相互之间独立的实体; Actor 可以通过消息来通信,一个 Actor 收到其他Actor的信息后,可以根据需要作出各种相应反应;消息的类型可以是任意的,消息的内容也可以是任意的;当一个 Actor 收到多个消息时,它先建立一个消息队列,将接收到的消息就放入队列,每次从队列中取出一个消息体进行处理。</p>    <p>Akka 是一个用 Scala 编写的库,用于简化编写容错的、高可伸缩性的 Actor 模型应用。Akka 使得开发人员可以更轻松地开发具有容错性、可扩展性和跨平台的并发程序,在工业界得到了广泛应用。</p>    <h3>2 Akka 入门</h3>    <p>下面我们将从一个学生老师例子出发,快速入门 Actor 模型。例子是从博客 <a href="/misc/goto?guid=4959731430797920088" rel="nofollow,noindex">rerun</a> 拿来的 (我就是看这个博客学习 Akka 的,强烈推荐英语好的同学看看)。这个例子有两个角色,一个是勤学好问的学生,一个是睿智的老师。每天早上,学生都会给老师发送一封邮件,向老师请教问题;而老师看到邮件之后,通过邮件给学生发送答案。在这个过程中,有几点需要注意:</p>    <p>1. 邮件一旦发送,就不能改变;</p>    <p>2. 学生和老师都按照自己的工作节奏检查邮箱;</p>    <p>3. 学生发送邮件之后,可以不等老师的回复。即工作流程可以是阻塞,也可以是非阻塞;</p>    <p>Akka 用于简化编写容错的、高可伸缩性的 Actor 模型应用,即我们很容易用 Akka 实现上述学生老师的 Actor 模型。我们先建立老师 Actor, 将老师 Actor 实现成一个服务(我是不是想歪了),代码如下所示。</p>    <pre>  <code class="language-java">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 的名,客户端需要用  }</code></pre>    <p>然后我们建立学生 Actor, 将学生 Actor 作为客户端。</p>    <pre>  <code class="language-java">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) // 假装一天过去了    }  }</code></pre>    <p>不管是服务端还是客户端,程序开始都从 lietal.conf 配置文件读入相应的配置。其中服务端读入 RemoteServerSideActor 的配置,而客户端读入 RemoteClientSideActor 的配置。lietal.conf 配置文件放在资源目录 src/main/resources,以便打包时打入包内。lietal.conf 的内容如下所示。</p>    <pre>  <code class="language-java">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        }      }    }  }</code></pre>    <p>从配置文件来看,如果我们在服务端启动两个老师 Actor, 他们会共用一个端口。我们很容易理解,所有发往老师 Actor 的消息都发往了服务器的一个端口,Akka 内部有一套机制将消息分发到不同的 Actor 中。这套机制就是 Akka 的 dispatcher,负责分发不同的消息到不同的 Actor。</p>    <p>完整项目代码已经上传到 <a href="/misc/goto?guid=4959731430891172224" rel="nofollow,noindex">Github</a> 上了,需要的同学自取。将完整项目打包之后,分别以 TearcherService 和 StudentClient 为主类运行程序,老师 Actor 和学生 Actor 之间的通信就运行起来了。下面是老师服务端运行的结果。</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/45d9b3f07db587e08345d87472fdff67.png"></p>    <p>下面是学生客户端运行的结果。</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/08d68ffdee43a6248f43783f9fb034be.png"></p>    <h3>3 总结</h3>    <p>一开始我只想实现一个单机版本的老师学生 Actor,实现之后发现不能体现 Akka 的特点,因此又实现一个网络版的老师学生 Actor。实现完网络版之后,稍微加深了对 Actor 之间消息传递的理解。</p>    <p> </p>    <p> </p>    <p>来自:http://www.algorithmdog.com/akka-usage-getstarted</p>    <p> </p>