一  Akka 简介

1  akka 的定义

Akka是JAVA虚拟机平台上构建高并发、分布式和容错应用的工具包, 是异步、非阻塞、高性能的事件驱动编程模型 / 通信框架Akka用Scala语言编写,同时提供了Scala和Java的开发接口。Akka处理并发的方法基于Actor模型,Actor之间通信唯一机制就是消息传递

在Akka中执行单元是Actor ,在Actor模型中并不是通过Actor对象的某个方法来告诉Actor需要做什么,而是给Actor发送一条消息。当一个Actor收到消息后,它有可能根据消息的内容做出某些行为,如更改自身状态,此时这个状态的更改是Actor自身进行的,并非由外界干预进行的。

2  Actor 的作用和特点

2.1   Actor是用来接收发送消息,处理逻辑的

2.2   ActorSystem 是单例的一个进程中只要有一个即可,它是用来创建Actor的,并且监控和管理他创建的Actor

 

2.3   Actor是多例的,可以创建多个实例

2.4   Actor编程模式就通过送消息来实现并发的 ,Actor 可以处理多种类型的消息

2.5   Akka就是基于Actor编程模型实现的,可以是单机,也可以是分布式

2.6   高容错(错误恢复) ,有一个 Actor 池 ,每个Actor 会处理不同的事件 ,Actor 做的事情可能会抛出异常 ,而它自己无法从中恢复 .这种情况下 ,需要再生产(created) 一个新的 Actor 来顶替它

二  Akka 通信原理

java 认证集群 java 集群 通信 框架_maven

1  先启动 Master (这是一个进程)

2  然后启动所有的 Worker(每个 Worker 都是一个进程)

3  Worker 向 Master 建立连接 ,然后向 Master 发送注册消息(workerID ,workerMemory ,workerCore)

4  Master 接收到 Worker 发送的注册消息后 ,将注册数据封装起来,然后保存;

5  Master 将 Worker 的注册数据保存后 ,将注册成功的消息返回给 Worker ;

6  Worker 接收到注册成功的消息后 ,开启定时器 ,定期(每10s)向 Master 发送心跳 ,以此证明自己没有掉线 ;

7  Master 接收到 Worker 发送的心跳信息之后 , 根据 Worker 提供的 workerID ,到 Map 里面找到对应的 workerInfo ,然后获取接收心跳时的当前系统的时间 ,更新 Worker 的最新的心跳时间 .

8  Master 机器上也有一个定时器 ,定期(每15s)检测自己身上所有的 Worker ,是否有超时的worker ,如果 Worker 超时了, 需要将超时的 workerID 移除 .

二 Akka在java端 Mawen 项目的安装

1  进入 IDEA 界面 ,在工具栏上点击 File--->New---->Project--->Maven---->Next

2  将项目名(Name) 和项目存储位置(Location) 设置好

3   将 Artifact Coordinates 展开 ,设置 GroupId (比如写成 com.akka.rpc)

4  最后点击 Finish ,即完成项目的设置 .

三 安装好Mawen 项目后 ,运行环境的准备

在 pom.xml 中,添加 akka 运行时需要的依赖(dependencies) ,和插件(build)

<!-- 常量 -->
    <properties>
        <maven.compiler.source>1.8</maven.compiler.source>
        <maven.compiler.target>1.8</maven.compiler.target>
        <encoding>UTF-8</encoding>
        <scala.version>2.12.10</scala.version>
        <scala.compat.version>2.12</scala.compat.version>
        <akka.version>2.4.17</akka.version>
    </properties>


    <dependencies>
        <!-- scala的依赖 -->
        <dependency>
            <groupId>org.scala-lang</groupId>
            <artifactId>scala-library</artifactId>
            <version>${scala.version}</version>
        </dependency>

        <!-- akka actor依赖 -->
        <dependency>
            <groupId>com.typesafe.akka</groupId>
            <artifactId>akka-actor_${scala.compat.version}</artifactId>
            <version>${akka.version}</version>
        </dependency>

        <!-- akka远程通信依赖 -->
        <dependency>
            <groupId>com.typesafe.akka</groupId>
            <artifactId>akka-remote_${scala.compat.version}</artifactId>
            <version>${akka.version}</version>
        </dependency>

    </dependencies>

    <build>
        <pluginManagement>
            <plugins>
                <!-- 编译scala的插件 -->
                <plugin>
                    <groupId>net.alchim31.maven</groupId>
                    <artifactId>scala-maven-plugin</artifactId>
                    <version>3.2.2</version>
                </plugin>
                <!-- 编译java的插件 -->
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <version>3.5.1</version>
                </plugin>
            </plugins>
        </pluginManagement>
        <plugins>
            <plugin>
                <groupId>net.alchim31.maven</groupId>
                <artifactId>scala-maven-plugin</artifactId>
                <executions>
                    <execution>
                        <id>scala-compile-first</id>
                        <phase>process-resources</phase>
                        <goals>
                            <goal>add-source</goal>
                            <goal>compile</goal>
                        </goals>
                    </execution>
                    <execution>
                        <id>scala-test-compile</id>
                        <phase>process-test-resources</phase>
                        <goals>
                            <goal>testCompile</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>

            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <executions>
                    <execution>
                        <phase>compile</phase>
                        <goals>
                            <goal>compile</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>


            <!-- 打jar插件 -->
            <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>

                                <!-- 指定maven方法 -->
                                <!--                                <transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">-->
                                <!--                                    <mainClass>cn._51doit.rpc.com.akka.akkalearning.Master</mainClass>-->
                                <!--                                </transformer>-->
                            </transformers>

                        </configuration>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

四  Akka 的入门程序(很重要!!!)

1  首先先创建 Master 的 Actor ,然后重写接收信息的方法

------继承Actor ,重写接收信息的方法--------
class Master03 extends Actor{
  --------用来接收信息的
  override def receive: Receive = {
    ------匹配到master启动的信息
    case "hello" => {
      println("接收到master启动的信息")
    }
    -------匹配到Worker发送注册的信息
    case "workersend" => {
      println("接收到Worker的启动成功的信息,并接收到其发送的注册信息")
      ------接收到信息后 ,将接收成功的信息返回给Worker
      sender() ! "mastersend"
    }
  }
}
--------先创建 master的 actor
object Master03{
  def main(args: Array[String]): Unit = {
    val masterHost3 = "localhost"
    val masterPort3 = 8888
    val configStr3 =
      s"""
         |akka.actor.provider = "akka.remote.RemoteActorRefProvider"
         |akka.remote.netty.tcp.hostname = $masterHost3
         |akka.remote.netty.tcp.port = $masterPort3
         |""".stripMargin
    val config3 = ConfigFactory.parseString(configStr3)
    ------创建 ActorSystem ,获得对象 ,然后再创建actor
    val actorSystem3 = ActorSystem("Master-Actor-System3", config3)
    ----------通过对象创建 actor
    val actorRef3 = actorSystem3.actorOf(Props[Master03], "Master-Actor3")
    ------先给自己g发送一条信息验证一下
    actorRef3 ! "hello"
  }
}

2  然后创建Worker 的 Actor

------继承actor ,重写连接方法和接收信息的方法
class Worker03 extends Actor{
  ------重写连接方法 ,连接 master,与其建立连接通道
  override def preStart(): Unit = {
    -----与master建立连接
    val actorSelection: ActorSelection = context.actorSelection("akka.tcp://Master-Actor-System3@localhost:8888/user/Master-Actor3")
    ------与master建立连接 ,并向其发送注册信息
    actorSelection ! "workersend"
  }
  ------用来接收信息的
  override def receive: Receive = {
    case "mastersend" => {
      println("接收到返回的注册成功的信息")
    }
  }
}
------先创建 Worker 的 actor
object Worker03{
  def main(args: Array[String]): Unit = {
    val workerHost3 = "localhost"
    val workerPort3 = 9999
    val configStr3 =
      s"""
         |akka.actor.provider = "akka.remote.RemoteActorRefProvider"
         |akka.remote.netty.tcp.hostname = $workerHost3
         |akka.remote.netty.tcp.port = $workerPort3
         |""".stripMargin
    -----获取config的对象
    val config3 = ConfigFactory.parseString(configStr3)
    -----创建ActorSystem ,获取对象,然后创建Worker的actor
    val actorSystem3 = ActorSystem("Worker-Actor-System3", config3)
    -----创建Worker的ACTOR
    actorSystem3.actorOf(Props[Worker03],"Worker-Actor3")
  }
}

五  akka的执行流程和案例(非常重要!!!)

1  akka的执行流程--详细版

1)  先创建 Master 的 Actor ,再创建 Worker 的 Actor ;

2)  先启动 Master (这是一个进程) ,后启动所有的 Worker(每个 Worker 都是一个进程)

3)  Worker 向 Master 建立网络通信的连接通道 ,并且 Worker 向 Master 发送注册消息(workerID ,workerMemory ,workerCore)

4)  Master 接收到 Worker 发送的注册消息后 ,将注册数据封装起来(以对象的形式 workerInfo(workerID ,workerMemory ,workerCore)) ,然后保存(以Map集合的形式保存 ,key是workerID ,value 是workerInfo ) , 注意 ,保存数据时需要确定Map 集合中没有该 workerID ,才能将其注册信息保存起来;

5)  Master 将 Worker 的注册数据保存后 ,将注册成功的消息返回给 Worker ;

6)  Worker 接收到注册成功的消息后 ,开启定时器 ,定期(每10s)向 Master 发送心跳(注 :这里是 Worker 先将心跳发送给自己 ,自己接收到心跳信息之后 ,再发送心跳给 Master ,附带上Worker 自己的 ID),以此证明自己没有掉线 ;

7)  Master 接收到 Worker 发送的心跳信息之后 , 根据 Worker 提供的 workerID ,到 Map 里面找到对应的 workerInfo ,然后获取接收心跳时的当前系统的时间 ,更新 Worker 的最新的心跳时间 .

8)  Master 机器上也有一个定时器 ,定期(每15s)检测自己身上所有的 Worker ,是否有超时的worker ,即定期发送消息给自己 ,通过过滤 Map 集合中的 workerInfo 中的最新的心跳时间 ,即用自己接收到检测信息的系统时间减去 Worker 最新一次的心跳时间 ,如果差值大于 10s ,即表示 Worker 超时了, 需要将超时的 workerID 从 Map 集合中移除 ,可以在此根据定期检查来定期的打印当前正在工作的 Worker 的个数 .

2  akka的程序案例

2.1  Master 端

import akka.actor.{Actor, ActorSystem, Props}
import com.typesafe.config.ConfigFactory
import scala.collection.mutable        ------创建可变 Map 集合时导入的包
import scala.concurrent.duration._     ------获取时间时需要手动导入的包

class Master extends Actor{

  -----Master 定期检查自己机器上的worker状态,将检查信息发送给自己--启动定时器
  override def preStart(): Unit = {
    import context.dispatcher
    ----启动定时器 ,定期检查超时的worker ,0s 延迟 ,15s 发送一次 ,发送给自己
    context.system.scheduler.schedule(0.milliseconds,15000.milliseconds,self,CheckTimeOutWorker)
  }

  -----建立一个map集合用来存储 Worker的注册信息
  val workerRegisterMap = new mutable.HashMap[String, WorkerInfo]()
  -----接收消息用的
  override def receive: Receive = {
    case WorkerRegister(workerId,workerMemory,workerCore) =>{
      -----接收worker发送的注册信息,然后将数据封装并保存
      val workerInfo = new WorkerInfo(workerId, workerMemory, workerCore)
      -----判断这个workerId是否存在在map集合中,如果不存在就将其保存到map集合里面
      if(!workerRegisterMap.contains(workerId)){
        workerRegisterMap(workerId) = workerInfo
      }
      -----Master将注册信息保存后,将Worker注册成功的信息返回给Worker
      sender() ! WorkerRegistered
    }
    -----接收到Worker发送过来的心跳信息
    case HeartBeat(workerId) => {
      -----根据workerID 到 Map集合中找到对应的workerInfo 的信息 ,然后更新一次心跳时间
      val workerInfo = workerRegisterMap(workerId)
      -----根据最新一次的心跳时间 ,就是系统当前的时间
      val currentTime = System.currentTimeMillis()
      -----更新workerInfo 的心跳时间
      workerInfo.lasterHeartBeatTime = currentTime
    }
    -----接收自己给自己发送的定期检查worker状态的信息
    case CheckTimeOutWorker => {
      -----检查所有的Worker ,如果超时了就是异常的,需要移除
      val deadWorker: Iterable[WorkerInfo] = workerRegisterMap.values.filter(w => System.currentTimeMillis() - w.lasterHeartBeatTime > 10000)
      -----移除超时的Worker
      deadWorker.foreach(w=>{
        -----从 Map集合中建超时的Workerid移除
        workerRegisterMap -= w.workerId
      })
      -----然后随时打印Worker工作的个数
      println(s"目前正在工作的worker的个数是${workerRegisterMap.size}")
    }
  }
}
object Master{
  val MASTER_ACTOR_SYSTEM ="MASTER_ACTOR_SYSTEM"
  val MASTER_ACTOR = "MASTER_ACTOR"

  def main(args: Array[String]): Unit = {
    val masterHost = args(0)
    val masterPort = args(1).toInt

    val configStr =
      s"""
         |akka.actor.provider = "akka.remote.RemoteActorRefProvider"
         |akka.remote.netty.tcp.hostname = $masterHost
         |akka.remote.netty.tcp.port = $masterPort
         |""".stripMargin
    -----获取config对象
    val config = ConfigFactory.parseString(configStr)
    -----创建ActorSystem
    val actorSystem = ActorSystem(MASTER_ACTOR_SYSTEM, config)
    -----创建Master的actor
    actorSystem.actorOf(Props[Master],MASTER_ACTOR)
  }
}

2.2  Worker端

import java.util.UUID
import akka.actor.{Actor, ActorSelection, ActorSystem, Props}
import com.typesafe.config.ConfigFactory
import scala.concurrent.duration._     -----获取系统时间时需要手动导入这个包

class Worker (val masterHost:String,val masterPort:Int,var workerMemory:Int,var workerCore:Int) extends Actor{
  var masterRef: ActorSelection = null
  -----调用一次 Worker ,就生成唯一的一个并且是随机生成的 workerId ,并将其转为字符串
  val workerId = UUID.randomUUID().toString
  -----先跟master建立连接
  override def preStart(): Unit = {
    masterRef = context.actorSelection("akka.tcp://${Master.MASTER_ACTOR_SYSTEM}@$masterHost:$masterPort/user/${Master.MASTER_ACTOR}")
    -----向 Master 发送注册信息
    masterRef ! WorkerRegister(workerId,workerMemory,workerCore)
  }
  -----用来接收消息的----------------
  override def receive: Receive = {
    -----接收到master返回的注册成功的信息
    case WorkerRegistered => {
      -----在worker内部启动一个定时器,定期向master发送心跳
      -----但是首先得发给自己 ,这里需要导入隐式转换
      import context.dispatcher
      -----0s 的延迟 ,10s 发送一次 ,先发送给自己
      context.system.scheduler.schedule(0 milliseconds,10000 milliseconds,self,SendHeartBeat)
    }
    case SendHeartBeat => {
      -----自己接收到自己给自己发送的心跳之后 ,将心跳发送给Master
      masterRef ! HeartBeat(workerId)
    }
  }
}
object Worker{
  val WORKER_ACTOR_SYSTEM = "WORKER_ACTOR_SYSTEM"
  val WORKER_ACTOR = "WORKER_ACTOR"

  def main(args: Array[String]): Unit = {
    val masterHost = args(0)
    val masterPort = args(1).toInt
    val workerHost = args(2)
    val workerPort = args(3).toInt
    val workerMemory = args(4).toInt
    val workerCores = args(5).toInt
    val configStr =
      s"""
         |akka.actor.provider = "akka.remote.RemoteActorRefProvider"
         |akka.remote.netty.tcp.hostname = $workerHost
         |akka.remote.netty.tcp.port = $workerPort
         |""".stripMargin
    -----获取Config对象
    val config = ConfigFactory.parseString(configStr)
    -----创建ActorSystem
    val actorSystem = ActorSystem("WORKER_ACTOR_SYSTEM", config)
    -----创建actor
    actorSystem.actorOf(Props(new Worker(masterHost,masterPort,workerMemory,workerCores)),"WORKER_ACTOR")
  }
}

2.3  WorkerInfo 

class WorkerInfo (val workerId:String ,val workerMemory:Int ,val workerCore:Int){

  var lasterHeartBeatTime:Long = _
}

2.4  需要的样例类

----Worker向Master发送注册信息
case class WorkerRegister(workerId:String ,workerMemory:Int,workerCore:Int)

----Master 向 Worker返回注册成功的消息
case object WorkerRegistered

----Worker先向自己发送心跳信息
case object SendHeartBeat

----Worker 向Master发送心跳信息
case class HeartBeat(workerId:String)

----Master 定期检查自己节点上Worker的状态是否在线
case object CheckTimeOutWorker

2.5  将程序打成 jar 包 ,Master 在windows 的"命令提示符窗口运行" 

1) 将编写的查询打成jar包 , 通过 Maven 中 package 的方法 ,将装有类和依赖的 jar 包(akka-1.0-SNAPSHOT.jar)复制粘贴/拖到桌面 ,然后重命名为akka.jar

注意 : 打 jar 包之后 ,会同时生成一个只有类的 jar 包 (original-akka-1.0-SNAPSHOT.jar) ,这个是没有依赖的 ,要使用并运行的话会相对麻烦 ,所以复制粘贴的时候需要注意 ,装有依赖的 jar 包才是我们需要的

2) 打开"命令提示符窗口"

C:\Users\123> java -cp C:\Users\123\Desktop\akka.jar akkareview.Master 192.168.0.163

window的网址可以在 "命令提示符窗口" 输入命令查看 : ipconfig    (IPv4 地址就是)    

可以直接将 jar 包拖入到"命令提示符窗口"中

java 认证集群 java 集群 通信 框架_scala_02

C:\Users\123>java -cp C:\Users\123\Desktop\akka.jar akkareview.Master 192.168.133.2 8888   
[INFO] [09/15/2020 15:44:09.188] [main] [akka.remote.Remoting] Starting remoting
[INFO] [09/15/2020 15:44:10.696] [main] [akka.remote.Remoting] Remoting started; listening on addresses :[akka.tcp://MASTER_ACTOR_SYSTEM@192.168.133.2:8888]
[INFO] [09/15/2020 15:44:10.701] [main] [akka.remote.Remoting] Remoting now listens on addresses: [akka.tcp://MASTER_ACTOR_SYSTEM@192.168.133.2:8888]
当前正在工作的worker的个数为 0   ----刚启动 Master 时的状态
[WARN] [SECURITY][09/15/2020 15:44:21.157] [MASTER_ACTOR_SYSTEM-akka.remote.default-remote-dispatcher-15] [akka.serialization.Serialization(akka://MASTER_ACTOR_SYSTEM)] Using the default Java serializer for class [akkareview.WorkerRegistered$] which is not recommended because of performance implications. Use another serializer or disable this warning using the setting 'akka.actor.warn-about-java-serializer-usage'
当前正在工作的worker的个数为 1    -----在第一个虚拟机上启动 Worker 10s 后
当前正在工作的worker的个数为 1    -----启动 Worker 20s 后
当前正在工作的worker的个数为 1    -----启动 Worker 30s 后
当前正在工作的worker的个数为 2    -----在第二个虚拟机上启动 Worker 10s 后
[WARN] [09/15/2020 15:45:16.020] [MASTER_ACTOR_SYSTEM-akka.remote.default-remote-dispatcher-6] [akka.tcp://MASTER_ACTOR_SYSTEM@192.168.133.2:8888/system/endpointManager/reliableEndpointWriter-akka.tcp%3A%2F%2FWORKER_ACTOR_SYSTEM%40192.168.133.4%3A9999-1] Association with remote system [akka.tcp://WORKER_ACTOR_SYSTEM@192.168.133.4:9999] has failed, address is now gated for [5000] ms. Reason: [Disassociated]
当前正在工作的worker的个数为 1    ----关闭一个虚拟机上的 Worker 15s后
[WARN] [09/15/2020 15:45:33.784] [MASTER_ACTOR_SYSTEM-akka.remote.default-remote-dispatcher-14] [akka.tcp://MASTER_ACTOR_SYSTEM@192.168.133.2:8888/system/endpointManager/reliableEndpointWriter-akka.tcp%3A%2F%2FWORKER_ACTOR_SYSTEM%40192.168.133.3%3A9999-0] Association with remote system [akka.tcp://WORKER_ACTOR_SYSTEM@192.168.133.3:9999] has failed, address is now gated for [5000] ms. Reason: [Disassociated]
当前正在工作的worker的个数为 1    ----关闭一个虚拟机上的 Worker 30s后
当前正在工作的worker的个数为 0    ----关闭第二个虚拟机上的 Worker 15s后

2.6 Worker 在虚拟机上运行

[root@linux03 ~]# java  -cp  /root/akka.jar  akkareview.Worker  192.168.0.163  8888  192.168.133.3  9999  4096  4

/root/akka.jar : 是指 jar 包在虚拟机上的路径 (通过命令 rz 将 windows 端的 jar 包上传到虚拟机上)

akkareview.Worker : 是指 jar 包在 java 端的路径 (Copy Reference)

192.168.0.163: 是指 Master 运行在 windows 命令提示符窗口上的网址     8888 : 是该网址对应的端口号

192.168.133.3 : 是指 Worker 运行在虚拟机上的网关的地址    9999是该网址对应的端口号(不同网址,端口号可以相同)

4096 : 是值虚拟机的内存       4 : 是指虚拟机处理器的内核数

2.7  Worker 在虚拟机上运行时遇到的问题和解决方法

2.7.1  jar 包的路径在输入时 ,写成 windows 上的存储路径 ,运行时会报错 !!

Error: Could not find or load main class day6.akkareview04.Worker04   ,解决的方案就是正确填写 jar 包在虚拟机上的路径.

2.7.1  输入运行 worker 的命令后出现闪退的信息 

[root@linux04 ~]# java -cp  /root/akka-1.0-SNAPSHOT.jar akkareview04.Worker04 192.168.133.2 8888 192.168.133.3 9999 4096 4 
[INFO] [09/15/2020 20:03:11.222] [main] [akka.remote.Remoting] Starting remoting
[ERROR] [09/15/2020 20:03:11.467] [WORKER_ACTOR_SYSTEM04-akka.remote.default-remote-dispatcher-6] [NettyTransport(akka://WORKER_ACTOR_SYSTEM04)] failed to bind to /192.168.133.3:9999, shutting down Netty transport
Exception in thread "main" org.jboss.netty.channel.ChannelException: Failed to bind to: /192.168.133.3:9999
        at org.jboss.netty.bootstrap.ServerBootstrap.bind(ServerBootstrap.java:272)
        at akka.remote.transport.netty.NettyTransport.$anonfun$listen$1(NettyTransport.scala:417)
        at scala.util.Success.$anonfun$map$1(Try.scala:255)
        at scala.util.Success.map(Try.scala:213)
        at scala.concurrent.Future.$anonfun$map$1(Future.scala:292)
        at scala.concurrent.impl.Promise.liftedTree1$1(Promise.scala:33)
        at scala.concurrent.impl.Promise.$anonfun$transform$1(Promise.scala:33)
        at scala.concurrent.impl.CallbackRunnable.run(Promise.scala:64)
        at akka.dispatch.BatchingExecutor$AbstractBatch.processBatch(BatchingExecutor.scala:55)
[ERROR] [09/15/2020 20:03:11.477] [main] [akka.remote.Remoting] Remoting error: [Startup failed] [
akka.remote.RemoteTransportException: Startup failed
        at akka.remote.Remoting.akka$remote$Remoting$$notifyError(Remoting.scala:145)
        at akka.remote.Remoting.start(Remoting.scala:211)
        at akka.remote.RemoteActorRefProvider.init(RemoteActorRefProvider.scala:212)
        at akka.actor.ActorSystemImpl.liftedTree2$1(ActorSystem.scala:810)
        at akka.actor.ActorSystemImpl._start$lzycompute(ActorSystem.scala:807)
        at akka.actor.ActorSystemImpl._start(ActorSystem.scala:807)
        at akka.actor.ActorSystemImpl.start(ActorSystem.scala:823)
        at akka.actor.ActorSystem$.apply(ActorSystem.scala:245)
        at akka.actor.ActorSystem$.apply(ActorSystem.scala:288)
        at akka.actor.ActorSystem$.apply(ActorSystem.scala:263)
        at day6.akkareview04.Worker04$.main(Worker04.scala:55)
        at day6.akkareview04.Worker04.main(Worker04.scala)
Caused by: org.jboss.netty.channel.ChannelException: Failed to bind to: /192.168.133.3:9999
        at org.jboss.netty.bootstrap.ServerBootstrap.bind(ServerBootstrap.java:272)
        at akka.remote.transport.netty.NettyTransport.$anonfun$listen$1(NettyTransport.scala:417)
        at scala.util.Success.$anonfun$map$1(Try.scala:255)
        at scala.util.Success.map(Try.scala:213)
        at scala.concurrent.Future.$anonfun$map$1(Future.scala:292)
        at scala.concurrent.impl.Promise.liftedTree1$1(Promise.scala:33)
        at scala.concurrent.impl.Promise.$anonfun$transform$1(Promise.scala:33)
        at scala.concurrent.impl.CallbackRunnable.run(Promise.scala:64)
        at akka.dispatch.BatchingExecutor$AbstractBatch.processBatch(BatchingExecutor.scala:55)
        at akka.dispatch.BatchingExecutor$BlockableBatch.$anonfun$run$1(BatchingExecutor.scala:91)
        at scala.runtime.java8.JFunction0$mcV$sp.apply(JFunction0$mcV$sp.java:23)
        at scala.concurrent.BlockContext$.withBlockContext(BlockContext.scala:85)
        at akka.dispatch.BatchingExecutor$BlockableBatch.run(BatchingExecutor.scala:91)
        at akka.dispatch.TaskInvocation.run(AbstractDispatcher.scala:39)
        at akka.dispatch.ForkJoinExecutorConfigurator$AkkaForkJoinTask.exec(AbstractDispatcher.scala:415)
        at java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:289)
        at java.util.concurrent.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:1056)
        at java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1692)
        at java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:157)
Caused by: java.net.BindException: Cannot assign requested address
        at sun.nio.ch.Net.bind0(Native Method)
        at sun.nio.ch.Net.bind(Net.java:433)
        at sun.nio.ch.Net.bind(Net.java:425)
        at sun.nio.ch.ServerSocketChannelImpl.bind(ServerSocketChannelImpl.java:223)
        at sun.nio.ch.ServerSocketAdaptor.bind(ServerSocketAdaptor.java:74)
        at org.jboss.netty.channel.socket.nio.NioServerBoss$RegisterTask.run(NioServerBoss.java:193)
        at org.jboss.netty.channel.socket.nio.AbstractNioSelector.processTaskQueue(AbstractNioSelector.java:391)
        at org.jboss.netty.channel.socket.nio.AbstractNioSelector.run(AbstractNioSelector.java:315)
        at org.jboss.netty.channel.socket.nio.NioServerBoss.run(NioServerBoss.java:42)
        at org.jboss.netty.util.ThreadRenamingRunnable.run(ThreadRenamingRunnable.java:108)
        at org.jboss.netty.util.internal.DeadLockProofWorker$1.run(DeadLockProofWorker.java:42)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
        at java.lang.Thread.run(Thread.java:748)
]       at akka.dispatch.BatchingExecutor$BlockableBatch.$anonfun$run$1(BatchingExecutor.scala:91)

        at scala.runtime.java8.JFunction0$mcV$sp.apply(JFunction0$mcV$sp.java:23)
        at scala.concurrent.BlockContext$.withBlockContext(BlockContext.scala:85)
        at akka.dispatch.BatchingExecutor$BlockableBatch.run(BatchingExecutor.scala:91)
        at akka.dispatch.TaskInvocation.run(AbstractDispatcher.scala:39)
        at akka.dispatch.ForkJoinExecutorConfigurator$AkkaForkJoinTask.exec(AbstractDispatcher.scala:415)
        at java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:289)
        at java.util.concurrent.ForkJoinPool$WorkQueue.runTask(ForkJoinPool.java:1056)
        at java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1692)
        at java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:157)
Caused by: java.net.BindException: Cannot assign requested address
        at sun.nio.ch.Net.bind0(Native Method)
        at sun.nio.ch.Net.bind(Net.java:433)
        at sun.nio.ch.Net.bind(Net.java:425)
        at sun.nio.ch.ServerSocketChannelImpl.bind(ServerSocketChannelImpl.java:223)
        at sun.nio.ch.ServerSocketAdaptor.bind(ServerSocketAdaptor.java:74)
        at org.jboss.netty.channel.socket.nio.NioServerBoss$RegisterTask.run(NioServerBoss.java:193)
        at org.jboss.netty.channel.socket.nio.AbstractNioSelector.processTaskQueue(AbstractNioSelector.java:391)
        at org.jboss.netty.channel.socket.nio.AbstractNioSelector.run(AbstractNioSelector.java:315)
        at org.jboss.netty.channel.socket.nio.NioServerBoss.run(NioServerBoss.java:42)
        at org.jboss.netty.util.ThreadRenamingRunnable.run(ThreadRenamingRunnable.java:108)
        at org.jboss.netty.util.internal.DeadLockProofWorker$1.run(DeadLockProofWorker.java:42)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
        at java.lang.Thread.run(Thread.java:748)
[INFO] [09/15/2020 20:03:11.499] [WORKER_ACTOR_SYSTEM04-akka.remote.default-remote-dispatcher-6] [akka://WORKER_ACTOR_SYSTEM04/system/remoting-terminator] Shutting down remote daemon.
[INFO] [09/15/2020 20:03:11.502] [WORKER_ACTOR_SYSTEM04-akka.remote.default-remote-dispatcher-6] [akka://WORKER_ACTOR_SYSTEM04/system/remoting-terminator] Remote daemon shut down; proceeding with flushing remote transports.
[INFO] [09/15/2020 20:03:11.506] [WORKER_ACTOR_SYSTEM04-akka.actor.default-dispatcher-5] [akka.remote.Remoting] Remoting shut down
[ERROR] [09/15/2020 20:03:11.507] [WORKER_ACTOR_SYSTEM04-akka.remote.default-remote-dispatcher-7] [akka.remote.Remoting] Remoting system has been terminated abrubtly. Attempting to shut down transports
[INFO] [09/15/2020 20:03:11.510] [WORKER_ACTOR_SYSTEM04-akka.remote.default-remote-dispatcher-7] [akka://WORKER_ACTOR_SYSTEM04/system/remoting-terminator] Remoting shut down.

这里出错的原因是虚拟机的网关输错了,在3号虚拟机上运行Worker ,但是虚拟机网关的地址写的是4号虚拟机的 ,解决方案就是 ,在哪个虚拟机上运行就填写哪个虚拟机的网关