一 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 通信原理
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 包拖入到"命令提示符窗口"中
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号虚拟机的 ,解决方案就是 ,在哪个虚拟机上运行就填写哪个虚拟机的网关