Akka是JAVA虚拟机JVM平台上构建高并发、分布式和容错应用的工具包。Akka用Scala语言写成,同时提供了Scala和JAVA的开发接口。
Akka处理并发的方法基于Actor模型。在Akka里,Actor之间通信的唯一机制就是消息传递
Spark的RPC是通过Akka类库实现的,Akka用Scala语言开发,基于Actor并发模型实现,Akka具有高可靠、高性能、可扩展等特点,使用Akka可以轻松实现分布式RPC功能。
负责管理的角色:ActorSystem(总监)单例的,在scala中就是一个object
负责通信的:Actor,多实例的(Student和School)
发送的消息:case class(参数)和case objce
发消息的方式:异步、同步
Akka-RPC通信案列思路:
- 第一步:在student和school类中,创建一个单列的ActorSystem,和一个实列的Actor.学生通过school的地址和端口号以及ActorSystem实现连接.
- 第二步:student向school发送注册信息(本文中,发送的是学生信息,这里需要创建一个case class StudentInformation(studentID: String, name: String, age: Int,gender: String)),school收到学生信息,保存,同时回复(case object StudentInformationed)用以表名收到信息,连接成功;
- 第三步:student和school内部都需要一个定时器,用于定时检测Actor心跳信息是否超时,超时一定时间,会自动剔除超时的Actor.
***
## 创建一个Scala代码如下
***
//学生发送给学校的学生信息
case class StudentInformation(studentID: String, name: String, age: Int, gender: String)
//学校收到学生信息,返回消息
case object StudentInformationed
//Student发送给School的心跳消息 因为需要识别是哪个Student需要有学生ID的参数,所以为case class
case class Heartbeat(studentID: String)
//-->第三步 Student 自己给自己发送的消息(内部消息)
case object SendHeartbeat
School发送给自己的消息,用于检查超时的student
case object CherkTimeOutStudent
***
## 创建一个Scala代码如下
***
class StudentInfor(val studentID: String, val name: String, var age: Int, val gender: String) {
//可变的成员变量
var LastUpdateTime: Long = _
override def toString = s"StudentInfor($studentID, $name, $age,$gender)"
}
***
## Student学生代码如下
***
import akka.actor.{Actor, ActorRef, ActorSelection, ActorSystem, Props}
import com.sun.xml.internal.ws.policy.privateutil.PolicyUtils.ConfigFile
import com.typesafe.config.{Config, ConfigFactory}
import scala.concurrent.duration._ //--->手动导包
class Student extends Actor {
var selection: ActorSelection = _ //-->,把selection局部变量转成成员变量.由val改成var
override def preStart(): Unit = {
selection = context.actorSelection("akka.tcp://School_Actor_System@localhost:4444/user/School_Actor")
// 第一步,学生给学校发送自己的注册信息(带有参数)
selection ! StudentInformation("w001", "yangshuaishuai", 18, "nan")
}
override def receive: Receive = {
//第三步 接受学校发的注册成功的消息,并启动定时器
case StudentInformationed => {
//导入隐式转换
import context.dispatcher
//启动一个定时器,定时向School发送心跳
//将消息发送给自己[定期执行的]
/**
* scheduler:调度员
* initialDelay: FiniteDuration,-->第一次延迟多长时间
* interval: FiniteDuration,-->多长时间执行一次
* receiver: ActorRef,-->发送给谁
* message: Any)(implicit-->发送的消息
*
*
* self :先自己给自己发消息,然后再给School发心跳.
*/
context.system.scheduler.schedule(0 millisecond, 5000 millisecond, self, SendHeartbeat)
}
//-->第四步 Student自己发送给自己的消息
case SendHeartbeat => {
//进行一些逻辑判断
//Student将心跳消息发送个School[周期性的]
selection ! Heartbeat("w001")
}
}
}
object Student {
def main(args: Array[String]): Unit = {
val host = args(0)
val port = args(1).toInt
val configStr =
s"""
|akka.actor.provider = "akka.remote.RemoteActorRefProvider"
|akka.remote.netty.tcp.hostname = "$host"
|akka.remote.netty.tcp.port = "$port"
""".stripMargin
val config: Config = ConfigFactory.parseString(configStr)
val student_Actor_Systrm: ActorSystem = ActorSystem("Student_Actor_Systrm", config)
val ref: ActorRef = student_Actor_Systrm.actorOf(Props[Student])
}
}
***
## School学校代码如下
***
import akka.actor.{Actor, ActorRef, ActorSystem, Props}
import com.typesafe.config.{Config, ConfigFactory}
import scala.collection.mutable
import scala.concurrent.duration._
class School extends Actor {
val idTostudent: mutable.HashMap[String, StudentInfor] = new mutable.HashMap[String, StudentInfor]
//--> 第六步 在preStart中启动定时器,定期检查超时的
override def preStart(): Unit = {
import context.dispatcher
//自己给自己发送用于检测超时的Student,那肯定的自己接
context.system.scheduler.schedule(0 millisecond, 10000 millisecond, self, CherkTimeOutStudent)
}
override def receive: Receive = {
//第二步 接受学生信息
case StudentInformation(studentID, name, age, gender) => {
//数据保存
val studentInfor = new StudentInfor(studentID, name, age, gender)
//将数剧信息放入Map中
idTostudent.put(studentID, studentInfor)
//School向学生返回一个连接成功的消息
sender() ! StudentInformationed
}
//--> 第五步 Student发送给School的心跳消息[周期性的]
case Heartbeat(studentID) => {
//根据StudentID到idTostudent中取查找对应的studentInfor
if (idTostudent.contains((studentID))) {
//根据ID取出studentInfor
val studentInfor = idTostudent(studentID)
//获取当前时间
val currentTime: Long = System.currentTimeMillis()
//更新最近一次心跳时间
studentInfor.LastUpdateTime = currentTime
}
}
// 第七步 自己接自己的消息
case CherkTimeOutStudent => {
//把超时的Student取出来
//去除当前时间
val currentTime = System.currentTimeMillis()
//遍历超时的Student
val students: Iterable[StudentInfor] = idTostudent.values
val deadstudents: Iterable[StudentInfor] = students.filter(w => currentTime - w.LastUpdateTime > 10000)
deadstudents.foreach(w => {
//移除超时的Studeng
idTostudent -= w.studentID
})
println(s"current alive student is:+${idTostudent.size}")
}
}
}
object School {
def main(args: Array[String]): Unit = {
val host = args(0)
val port = args(1).toInt
val configStr =
s"""
|akka.actor.provider = "akka.remote.RemoteActorRefProvider"
|akka.remote.netty.tcp.hostname = "$host"
|akka.remote.netty.tcp.port = "$port"
""".stripMargin
val config: Config = ConfigFactory.parseString(configStr)
val actorSystem: ActorSystem = ActorSystem("School_Actor_System", config)
val school_Actor: ActorRef = actorSystem.actorOf(Props[School], "School_Actor")
}
}