Scala消息通信之akka,akka案例

来源:互联网 发布:看程序员直播 编辑:程序博客网 时间:2024/05/10 07:22

Scala编程实战

一、 课程目标

1. 目标:熟练使用Scala编写程序

这里写图片描述

二、 项目概述

1. 需求

目前大多数的分布式架构底层通信都是通过RPC实现的,RPC框架非常多,比如前我们学过的Hadoop项目的RPC通信框架,但是Hadoop在设计之初就是为了运行长达数小时的批量而设计的,在某些极端的情况下,任务提交的延迟很高,所有Hadoop的RPC显得有些笨重。

Spark 的RPC是通过Akka类库实现的,Akka用Scala语言开发,基于Actor并发模型实现,Akka具有高可靠、高性能、可扩展等特点,使用Akka可以轻松实现分布式RPC功能。

2. Akka简介

Akka基于Actor模型,提供了一个用于构建可扩展的(Scalable)、弹性的(Resilient)、快速响应的(Responsive)应用程序的平台。

Actor模型:在计算机科学领域,Actor模型是一个并行计算(Concurrent Computation)模型,它把actor作为并行计算的基本元素来对待:为响应一个接收到的消息,一个actor能够自己做出一些决策,如创建更多的actor,或发送更多的消息,或者确定如何去响应接收到的下一个消息。
这里写图片描述
Actor是Akka中最核心的概念,它是一个封装了状态和行为的对象,Actor之间可以通过交换消息的方式进行通信,每个Actor都有自己的收件箱(Mailbox)。通过Actor能够简化锁及线程管理,可以非常容易地开发出正确地并发程序和并行系统,Actor具有如下特性:

1.提供了一种高级抽象,能够简化在并发(Concurrency)/并行(Parallelism)应用场景下的编程开发
2.提供了异步非阻塞的、高性能的事件驱动编程模型
3.超级轻量级事件处理(每GB堆内存几百万Actor)

案例介绍:

知识点说明:
1.Akka可以实现不同进程之间的通信
2.老大叫ActorSystem,用于创建和监控Acotr
3.真正用于通信的是Acotr
4.一个进程里面可以有多个Acotor
5.可以有多个进程
6.如果不同ActorSystem下面的Acotor要进行通信,首先AcotorSystem之间要建立连接

在akka的程序中,需要服务端和客户端。
创建一个maven项目,其中maven项目的pom.xml内容如下:

<?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0"         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">    <modelVersion>4.0.0</modelVersion>    <groupId>cn.toto.akka</groupId>    <artifactId>MyRPC</artifactId>    <version>1.0-SNAPSHOT</version>    <properties>        <maven.compiler.source>1.7</maven.compiler.source>        <maven.compiler.target>1.7</maven.compiler.target>        <encoding>UTF-8</encoding>        <scala.version>2.10.6</scala.version>        <scala.compat.version>2.10</scala.compat.version>    </properties>    <dependencies>        <dependency>            <groupId>org.scala-lang</groupId>            <artifactId>scala-library</artifactId>            <version>${scala.version}</version>        </dependency>        <dependency>            <groupId>com.typesafe.akka</groupId>            <artifactId>akka-actor_2.10</artifactId>            <version>2.3.14</version>        </dependency>        <dependency>            <groupId>com.typesafe.akka</groupId>            <artifactId>akka-remote_2.10</artifactId>            <version>2.3.14</version>        </dependency>    </dependencies>    <build>        <!-- 源码包放置的位置 -->        <sourceDirectory>src/main/scala</sourceDirectory>        <testSourceDirectory>src/test/scala</testSourceDirectory>        <plugins>            <!--专门用于编译scala的插件-->            <plugin>                <groupId>net.alchim31.maven</groupId>                <artifactId>scala-maven-plugin</artifactId>                <version>3.2.2</version>                <executions>                    <execution>                        <goals>                            <goal>compile</goal>                            <goal>testCompile</goal>                        </goals>                        <configuration>                            <args>                                <arg>-make:transitive</arg>                                <arg>-dependencyfile</arg>                                <arg>${project.build.directory}/.scala_dependencies</arg>                            </args>                        </configuration>                    </execution>                </executions>            </plugin>            <!--主要使用来的打包的插件-->            <plugin>                <groupId>org.apache.maven.plugins</groupId>                <artifactId>maven-shade-plugin</artifactId>                <version>2.4.3</version>                <executions>                    <execution>                        <phase>package</phase>                        <goals>                            <goal>shade</goal>                        </goals>                        <configuration>                            <filters>                                <filter>                                    <artifact>*:*</artifact>                                    <!--表示把下面的这些文件给删掉-->                                    <excludes>                                        <exclude>META-INF/*.SF</exclude>                                        <exclude>META-INF/*.DSA</exclude>                                        <exclude>META-INF/*.RSA</exclude>                                    </excludes>                                </filter>                            </filters>                            <transformers>                                <transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">                                    <resource>reference.conf</resource>                                </transformer>                                <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">                                    <mainClass>cn.toto.akka.Worker</mainClass>                                </transformer>                            </transformers>                        </configuration>                    </execution>                </executions>            </plugin>        </plugins>    </build></project>

其中服务端的代码如下:

package cn.toto.akkaimport akka.actor.{Actor, ActorSystem, Props}import com.typesafe.config.ConfigFactory/**  * Created by toto on 2017/7/2.  */class Master extends Actor {  override def receive: Receive = {    //里面要有一个偏函数    case "start" => {      println("starting...")      println("started....")    }    case "stop" => {      println("stoping...")      println("stopted...");    }    //master接收到worker的消息    case "connect" => {      println("a client connected...")      //这里表示向客户端发送一个消息      sender ! "success"    }    case _ => println("123")  }}object Master {  def main(args: Array[String]): Unit = {    //通过s,可以将变量通过$取到    val host = "127.0.0.1"    val port = "8888"    val confStr =      s"""         |akka.actor.provider = "akka.remote.RemoteActorRefProvider"         |akka.remote.netty.tcp.hostname = "$host"         |akka.remote.netty.tcp.port = "$port"       """.stripMargin    //读取一些默认的配置文件    val conf = ConfigFactory.parseString(confStr)    //ActorSystem单例的,用于创建Actor并监控actor    val actorSystem = ActorSystem("MasterActorSystem",conf)    //通过ActorSystem创建Actor,可以通过Ctrl + P的方式看到这个方法里面可以有哪些参数    //通过Master类型,反射创建实例    var master = actorSystem.actorOf(Props[Master],"Master")    //接着可以发消息了。后续这里可以不是发字符串,可以发case class    master ! "start"    master ! "hello"    master ! "stop"    actorSystem.awaitTermination()  }}

另外,worker端的代码如下:

package cn.toto.akkaimport akka.actor.{Actor, ActorSystem, Props}import com.typesafe.config.ConfigFactory/**  * Created by toto on 2017/7/2.  */class Worker extends Actor{  //actor里面有声明周期方法  //preStart在构造器之后receive之前执行  override def preStart(): Unit = {    //首先跟Master建立连接,其中"akka.tcp://MasterActorSystem@127.0.0.1:8888"可以在Master运行之后的控制台中找到    //下面的地址:"akka.tcp://MasterActorSystem@127.0.0.1:8888"表示要先连接actorSystem,接着要和它下面的/user/Master建立通信    //这样就相当于拿到了master的代理对象    val master = context.actorSelection("akka.tcp://MasterActorSystem@127.0.0.1:8888/user/Master")    //通过mater的引用向Master发送消息    //向master发送connect消息    master ! "connect"  }  override def receive: Receive = {    case "success" => {      println("a msg form master:success")    }  }}object Worker {  def main(args: Array[String]): Unit = {    //通过s,可以将变量通过$取到    //val host = args(0)    //val port = args(1).toInt    val host = "127.0.0.1"    val port = "9999"    val confStr =      s"""         |akka.actor.provider = "akka.remote.RemoteActorRefProvider"         |akka.remote.netty.tcp.hostname = "$host"         |akka.remote.netty.tcp.port = "$port"       """.stripMargin    val conf = ConfigFactory.parseString(confStr)    //单例的ActorSystem    val actorSystem = ActorSystem("WorkerActorSystem",conf)    //通过actorSystem来创建actor    val worker = actorSystem.actorOf(Props[Worker],"Worker")    //这里是等待优雅退出    actorSystem.awaitTermination()  }}

运行过程:
1、先启动Master(右键run),运行后的效果如下:
这里写图片描述
2、接着运行worker(右键run),运行后的效果如下:
这里写图片描述

原创粉丝点击