利用Akka的actor编程模型,实现2个进程间的通信。
1.架构图
2.重要类介绍
ActorSystem:在Akka中,ActorSystem是一个重量级的结构,他需要分配多个线程,所以在实际应用中,ActorSystem通常是一个单例对象,我们可以使用这个ActorSystem创建很多Actor。
注意:
(1)、ActorSystem是一个进程中的老大,它负责创建和监督actor
(2)、ActorSystem是一个单例对象
(3)、actor负责通信
3.Actor
在Akka中,Actor负责通信,在Actor中有一些重要的生命周期方法。
(1)preStart()方法:该方法在Actor对象构造方法执行后执行,整个Actor生命周期中仅执行一次。
(2)receive()方法:该方法在Actor的preStart方法执行完成后执行,用于接收消息,会被反复执行。
4.实现步骤
5.具体代码
(1)master端代码
import akka.actor.{Actor, ActorSystem, Props}
import com.typesafe.config.ConfigFactory
//todo:利用akka中的actor模型来实现2个进程间的通信----master端
class Master extends Actor{
//不在任何方法中的代码,称为构造代码
//master构造代码先执行
println("Master constructor invoked")
//构造代码后执行,类似一个初始化的方法
override def preStart(): Unit = {
println("preStart method invoked")
}
//receive方法会在preStart方法执行后被调用,在这里表示不断的接收消息
override def receive: Receive = {
case "start" =>{
println("a client connected")
}
case "register" =>{
println("a client register")
//master 反馈注册成功的信息给worker sender()表示worker的引用
sender() ! "success"
}
}
}
object Master{
def main(args: Array[String]): Unit = {
//定义master的ip地址和端口 参数:127.0.0.1 8888
val host = args(0)
val port = args(1)
//准备配置信息 shift+英文上引号 3次
val configStr: String =
s"""
|akka.actor.provider = "akka.remote.RemoteActorRefProvider"
|akka.remote.netty.tcp.hostname = "$host"
|akka.remote.netty.tcp.port = "$port"
""".stripMargin
//构建一个config对象
val config = ConfigFactory.parseString(configStr)
//1.创建ActorSystem老大,它负责创建和监督子Actor
val masterActorSystem = ActorSystem("masterActorSystem",config)
//2.基于masterActorSystem 来创建master actor
val masterActor = masterActorSystem.actorOf(Props(new Master),"masterActor")
//3.测试 向master actor发送一个消息
masterActor ! "start"
}
}
(2)worker端代码
import akka.actor.{Actor, ActorRef, ActorSelection, ActorSystem, Props}
import com.typesafe.config.ConfigFactory
//todo:利用akka来实现actor模型来实现2个进程间的通信 --worker端
class Worker extends Actor{
//构造代码先运行
println("Worker constructor invoked")
//preStart方法会在构造代码执行后被调用 只会被执行一次
override def preStart() : Unit = {
println("preStart method invoked")
//获取到master的引用
//通过ActorContext上下文对象,调用actorSelection从已存在actor中找到目标actor 方法中需要一个字符串
//这个字符串包括了:1.通信协议 2.master的ip 3.master端口
//4.master actor的名称 5.创建master actor的老大 ActorSystem 6.actor层级关系
val master: ActorSelection = context.actorSelection(
"akka.tcp://masterActorSystem@127.0.0.1:8888/user/masterActor")
//向master发送注册信息
master ! "register"
}
override def receive: Receive = {
case "start" => {
println("a client connected")
}
//worker接收master注册成功的信息
case "success" =>{
println("register success")
}
}
}
object Worker{
def main(args: Array[String]): Unit = {
//定义worker的ip地址和端口 参数:127.0.0.1 6666
val host = args(0)
val port = args(1)
val configStr: String =
s"""
|akka.actor.provider = "akka.remote.RemoteActorRefProvider"
|akka.remote.netty.tcp.hostname = "$host"
|akka.remote.netty.tcp.port = "$port"
""".stripMargin
val config = ConfigFactory.parseString(configStr)
//1.创建ActorSystem
val workerActorSystem : ActorSystem = ActorSystem("workerActorSystem",config)
//2.创建worker actor
val workerActor: ActorRef = workerActorSystem.actorOf(Props(new Worker),"workerActor")
//3.测试
workerActor ! "start"
}
}
先启动master,再启动worker,运行结果如下
master输出:
[INFO] [09/03/2019 09:06:59.805] [main] [Remoting] Starting remoting
[INFO] [09/03/2019 09:07:00.857] [main] [Remoting] Remoting started; listening on addresses :[akka.tcp://masterActorSystem@127.0.0.1:8888]
[INFO] [09/03/2019 09:07:00.859] [main] [Remoting] Remoting now listens on addresses: [akka.tcp://masterActorSystem@127.0.0.1:8888]
Master constructor invoked
preStart method invoked
a client connected
a client register
worker输出:
[INFO] [09/03/2019 09:08:55.068] [main] [Remoting] Starting remoting
[INFO] [09/03/2019 09:08:56.044] [main] [Remoting] Remoting started; listening on addresses :[akka.tcp://workerActorSystem@127.0.0.1:6666]
[INFO] [09/03/2019 09:08:56.045] [main] [Remoting] Remoting now listens on addresses: [akka.tcp://workerActorSystem@127.0.0.1:6666]
Worker constructor invoked
preStart method invoked
a client connected
register success