AKKA简介
- AKKA介绍:
- AKKA是java虚拟机JVM平台上构建高并发、分布式和容错应用工具包和运行时。可以理解为Akka是编写并发程序的框架
- AKKA是scala语言写成,同时提供了scala和java的接口
- AKKA主要解决的问题是:可以轻松的写出高效稳定的并发程序,程序员不在 过多的考虑县城,锁,和资源竞争等细节
- 主要解决什么问题
- 处理并发问题保证共享数据的一致性和正确性,因为程序是多线程时,多线程同事对一个数据进行修改,如果不加锁等同步条件,实际上大并发就会阻塞在这段代码中,对程序效率有很大影响
- 若是用单线程处理,不会有数据一致性问题,但是系统的性能又不能保证
- Actor模型的出现解决了这个问题,简化并发问题,提升程序性能,是一种并发问题的解决方案。
- Akka Actor的模型
- AKKA处理并发的方法基于Actor模型
- 在基于actor的系统里,所有的事物是actor,就好像在面向对象设计里面所有的事物都是对象一样
- Actor模型是作为一个并发模型设计和架构的,actor于actor之间只能通过消息通信
- Actor于Actor之间只能用消息通信,消息是有顺序的
- ActorSystem 的职责是负责创建并管理起创建的Actor,在一个JVM进行中有一个即可
- Actor模型是对并发模型进行了高的抽象
- Actor模型是异步,非阻塞,高性能的事件驱动编程模型
- Actor模型是轻量级时间处理,1GB内存可容纳百万级别个Actor。因此处理大并发性能很高
- 模型工作机制说明
Actor模型说明
1.ActorSystem创建Actor
2.ActorRef:可以理解成是Actor的代理或者引用,消息是通过ActoRef来发送的,而不能通过Actor直接发送消息,通过哪个ActorRef发消息就说明把该消息发送给哪个Actor
3.消息发送 到Dispatch Message(消息分发器,可以理解为一个线程池),它得到消息后会将消息进行分发到对应的MailBox(可以理解为一个消息队列,FIFO)
4.Actor可以通过receive方法来获取消息,然后进行处理
Actor间消息传递机制
1.每个消息就是一个message对象,message继承了Runnable,因为Message就是线程类
2.从Actor模型工作机制看上去很麻烦,但是程序员编程时只需要编写Actor就可以了,其他的交给Actor模型完成即可
3.A Actor要给B Actor发送消息,那么 A Actor要先拿到B Actor的代理对象ActorRef才能发送对象
测试用例:
package analyse import akka.actor.{Actor, ActorRef, ActorSystem, Props} import com.typesafe.config.ConfigFactory class YellowClikenServer extends Actor { override def receive: Receive = { case "start" => println("start 小黄鸡客服开始工作了...") // 如果接收到了服务端的发来的消息,即 ClientMessage case ClientMessage(mes) => { println("客户咨询的问题是:" + mes) mes match { // 使用 match case 匹配(模糊匹配) case "价格" => sender() ! ServerMessage("20000 RMB") case "地址" => sender() ! ServerMessage("北京市") case "技术" => sender() ! ServerMessage("大数据") case _ => sender() ! ServerMessage("你说的啥子:)") } } } } // 主程序入口 object YellowChickenServerApp extends App { val host = "127.0.0.1" // 服务端ip地址 val port = 9999 // 端口 // 创建 config 对象,指定协议类型、监听的ip和端口 val config = ConfigFactory.parseString( s""" |akka.actor.provider="akka.remote.RemoteActorRefProvider" |akka.remote.netty.tcp.hostname=$host |akka.remote.netty.tcp.port=$port """.stripMargin) // 创建 ActorSystem val serverActorSystem = ActorSystem("Server", config) // 创建 YellowChickenServer 的 Actor 和 ActorRef val yellowChickenServerActorRef: ActorRef = serverActorSystem.actorOf(Props[YellowClikenServer], "YellowChickenServer-01") // 启动服务端 yellowChickenServerActorRef ! "start" }
package analyse import akka.actor.{Actor, ActorRef, ActorSelection, ActorSystem, Props} import com.typesafe.config.ConfigFactory import scala.io.StdIn class CustomerActor(serverHost: String, serverPort: Int) extends Actor { // 定义一个 YellowChickenServerRef var serverActorRef: ActorSelection = _ // 在 Actor 中有一个方法 preStart 方法,它会在 Actor 运行前执行 // 在 Akka 开发中,通常将初始化的工作,放在 preStart 方法中 override def preStart(): Unit = { this.serverActorRef = context.actorSelection(s"akka.tcp://Server@${serverHost}:${serverPort}/user/YellowChickenServer-01") println("this.serverActorRefer=" + this.serverActorRef) } override def receive: Receive = { case "start" => println("start 客户端运行,可以咨询问题") case mes: String => { // 发给服务端 // serverActorRef ! mes // 不应该发送字符串,应该包装一把,应该发送一个(样例)对象(即协议) serverActorRef ! ClientMessage(mes) // 此时发送的是一个对象,该样例类默认实现了序列化 和 apply 方法 } // 如果接受到了服务器端的消息 case ServerMessage(mes) => { println(s"收到小黄鸡客服(Server)消息:$mes") } } } // 主程序入口 object CustomerActorApp extends App { val (host, port, serverHost, serverPort) = ("127.0.0.1", 9990, "127.0.0.1", 9999) val config = ConfigFactory.parseString( s""" |akka.actor.provider="akka.remote.RemoteActorRefProvider" |akka.remote.netty.tcp.hostname=$host |akka.remote.netty.tcp.port=$port """.stripMargin) // 创建 ActorSystem val clientActorSystem = ActorSystem("Client", config) // 创建 CustomerActor 的 Actor 和 ActorRef val clientActorRef: ActorRef = clientActorSystem.actorOf(Props(new CustomerActor(serverHost, serverPort)), "CustomerActor-01") // 启动客户端 clientActorRef ! "start" // 客户端发送消息 while (true) { val mes = StdIn.readLine() clientActorRef ! mes } }
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步