mailbox 编程_Scala 并发编程模型Akka总结

本文介绍了Akka,一个用于构建高并发、分布式和容错应用的框架,重点讲解了Actor模型的工作原理,包括Actor之间的消息通信、ActorSystem的管理以及ActorRef的使用。此外,还阐述了Akka的Dispatchers,如默认Dispatcher、Balancing Dispatcher、Pinned Dispatcher和Calling Thread Dispatcher的特点和应用场景。最后提到了Router的概念,如RoundRobinRouter、RandomRouter等,以及如何创建Router。
摘要由CSDN通过智能技术生成

一、Akka简介

Akka是在JVM平台上构建的高并发、分布式和容错应用的工具,也可以理解成是编写并发程序的框架,它用Scala语言写成,主要解决的问题是:轻松写出高效稳定的并发程序,使用者不再过多的考虑线程、锁和资源竞争等细节。

ed00d1d86171a94c786c762a0e37dc72.png

https://akka.io 官网截图

二、Actor模型

处理并发问题关键是要保证共享数据的一致性和正确性,因为程序是多线程时,多个线程对同一个数据进行修改,若不加同步条件,势必会造成数据污染。但是当我们对关键代码加入同步条件synchronized 后,实际上大并发就会阻塞在这段代码,对程序效率有很大影响。若是用单线程处理,不会有数据一致性的问题,但是系统的性能又不能保证。Actor 模型的出现解决了这个问题,简化并发编程,提升程序性能。

三、Akka中的Actor模型

3.1 Actor模型及其说明

7d5b2770b65a4c0ca97efbe3c90a6fe1.png

  • Akka 处理并发的方法基于 Actor 模型(如上图)。

  • 在基于 Actor 的系统里,所有的事物都是 Actor,就好像在面向对象设计里面所有的事物都是对象一样。

  • Actor 模型是作为一个并发模型设计和架构的。Actor 与 Actor 之间只能通过消息通信,如图的信封(MailBox)。

  • Actor 与 Actor 之间只能用消息进行通信,当一个 Actor 给另外一个 Actor发消息,消息是有顺序的(消息队列),只需要将消息投寄的相应的邮箱即可。

  • 怎么处理消息是由接收消息的Actor决定的,发送消息Actor可以等待回复,也可以异步处理【类似于ajax】

  • ActorSystem 的职责是负责创建并管理其创建的 Actor, ActorSystem 是单例的(可以ActorSystem是一个工厂,专门创建Actor),一个 JVM 进程中有一个即可,而 Actor 是可以有多个的。

  • Actor模型是对并发模型进行了更高的抽象。

  • Actor模型是异步、非阻塞、高性能的事件驱动编程模型。[案例: 说明 什么是异步、非阻塞, 最经典的案例就是ajax异步请求处理 ]

  • Actor模型是轻量级事件处理(1GB 内存可容纳百万级别个 Actor),因此处理大并发性能高.

3.2  Actor模型工作机制说明

fa7ac0bf0a4b88946d004a615d409547.png

Actor模型的工作机制:

  • ActorySystem创建Actor

  • ActorRef:可以理解成是Actor的代理或者引用。消息是通过ActorRef来发送,而不能通过 Actor 发送消息,通过哪个ActorRef 发消息,就表示把该消息发给哪个Actor

  • 消息发送到Dispatcher Message (消息分发器),它得到消息后,会将消息进行分发到对应的MailBox。(注: Dispatcher Message 可以理解成是一个线程池, MailBox 可以理解成是消息队列,可以缓冲多个消息,遵守FIFO)

  • Actor 可以通过 receive方法来获取消息,然后进行处理。

Actor模型的消息机制:

  • 每一个消息就是一个Message对象。Message 继承了Runable, 因为Message就是线程类。

  • 从Actor模型工作机制看上去很麻烦,但是程序员编程时只需要编写Actor就可以了,其它的交给Actor模型完成即可。

  • A Actor要给B Actor 发送消息,那么A Actor 要先拿到(也称为持有) B Actor 的 代理对象ActorRef 才能发送消息 

四、Akka 中的 Dispatchers

Akka 中的 Dispatcher ,是维持Akka Actor动作的核心组件,是整个Akka框架的引擎。它是基于Java的Executor框架来实现的,Dispatcher 控制和协调消息并将其分发给运行在底层线程上的Actor,由它来负责调度资源的优化,并保证任务以最快的速度执行,用一句话来说,Dispatcher 负责 Akka 中的资源调度功能。

跟线程池一样,不自己创建Dispatcher时使用的是默认Dispathcer,如果任务中有些耗时任务,容易将默认线程池打满,影响其他任务的调度。

Dispatcher一共有四种,分别是:

(1) Dispatcher

Dispatcher是Akka中默认的派发器,它是基于事件的分发器,该派发器绑定一组Actor到线程池中。该派发器有如下特点:

  • 每一个Actor都有自己的邮箱

  • 该派发器都可以被任意数量的Actor共享

  • 该派发器可以由ThreadPoolExecutor或ForkJoinPool提供支持

  • 该派发器是非阻塞的。

(2) Balancing Dispatcher

该派发器是基于事件的分发器,它会将任务比较多的Actor的任务重新分发到比较闲的Actor上运行。该派发器有如下特点:

  • 所有Actor共用一个邮箱

  • 该派发器只能被同一种类型的Actor共享

  • 该派发器可以由ThreadPoolExecutor或ForkJoinPool提供支持

(3) Pinned Dispatcher

该派发器为每一个Actor提供一个单一的、专用的线程。这种做法在I/O操作或者长时间运行的计算中很有用。该派发器有如下特点:

  • 每一个Actor都有自己的邮箱

  • 每一个Actor都有专用的线程,该线程不能和其他Actor共享

  • 该派发器有一个Executor线程池

  • 该派发器在阻塞上进行了优化,如:如果程序正在进行I/O操作,那么这个Actor将会等到任务执行完成。这种阻塞型的操作在性能上要比默认的Dispatcher要好。

(4) Calling Thread Dispatcher

该派发器主要用于测试,并且在当前线程运行任务,不会创建新线程,该派发器有如下特点:

  • 每一个Actor都有自己的邮箱

  • 该派发器都可以被任意数量的Actor共享

  • 该派发器由调用线程支持

关于dispatcher的更多介绍可参考官网介绍:https://doc.akka.io/docs/akka/current/typed/dispatchers.html

五、Akka 中的 Router

当处理到来的消息流时,我们需要一个actor来引导消息路由到目标actor,从而提高消息的分配效率。在Akka中这个 actor就是Router。它所管理的一些目标actor叫做routees,Akka定义好的一些Router:

  • akka.routing.RoundRobinRouter:轮转路由器将消息按照轮转顺序发送给routers

  • akka.routing.RandomRouter:随机路由器随机选择一个router,并将消息发送给这个router

  • akka.routing.SmallestMailboxRouter:最小邮箱路由器会在routers中选择邮箱里信息最少的router,然后把消息发送给它。

  • akka.routing.BroadcastRouter:广播路由器将相同的消息发送给所有的routers

  • akka.routing.ScatterGatherFirstCompletedRouter:敏捷路由器先将消息广播到所有routers,返回最先完成任务的router的结果给调用者。

创建router actor 有两种方式:

  • Pool(池)——routees都是router 的子actor,如果routees终止,router将把它们移除,pool方式创建Router:

def main(args: Array[String]): Unit = {  // 创建router  val actorSystem = ActorSystem("testRouter")  // 通知代码来实现路由器  val poolRouter = actorSystem.actorOf(RoundRobinPool(5).props(Props[WorkerRoutee]),"router")  hahaRouter ! RouteeMsg(333)    val myRouter = actorSystem.actorOf(Props[WorkerRoutee].withRouter(RoundRobinPool(nrOfInstances = 5)))  myRouter ! RouteeMsg(22)  val masterRouter = actorSystem.actorOf(Props[MasterRouter],"masterRouter")  masterRouter ! RouteeMsg(100)}
  • Group(群组)——routees都创建在router的外部,router通过使用actor来选择将消息发送到指定路径,但不监管routees是否终止。Router actor 向 routees 发送消息,与向普通actor发送消息一样通过其ActorRef。Router actor 不会改变消息的发送人,routees 回复消息时发送回原始发件人,而不是Router actor。

import akka.actor._import akka.routing.{ RoundRobinGroup}object HelloScala {  def main(args: Array[String]): Unit = {    val _system = ActorSystem("AkkaTestActor")    val tActor = _system.actorOf(Props[TestActor],"testActor")    tActor ! RouteeMsg(13333) }}class TestActor extends  Actor{  val routee1 = context.actorOf(Props[WorkerRoutee],"w1")  val routee2 = context.actorOf(Props[WorkerRoutee],"w2")  val routee3 = context.actorOf(Props[WorkerRoutee],"w3")  val paths: Array[String] = Array(routee1.path.toString,routee2.path.toString,routee3.path.toString)  val testRouter = context.actorOf(RoundRobinGroup(paths).props(),"testRouter")  override def receive = {    case RouteeMsg(s) =>      testRouter ! RouteeMsg(s)    case _ =>  }}// 定义routee对应的actor类型case class RouteeMsg(s: Int)class WorkerRoutee extends Actor{  override def receive: Receive = {    case RouteeMsg(s) =>      println(s"${self.path} mesage#$s")      val caleActor = context.actorOf(Props[Cale])      caleActor ! RouteeMsg(s)    case _ =>      println(s"${self.path}")  }}class Cale extends Actor{  override def receive: Receive = {    case RouteeMsg(s) =>      println(s"${self.path} message#$s")    case _ =>      println(s"${self.path}")  }}

上面几部分,简单介绍了Akka 模型中的几个重要组成部分以及实例用法,当然akka的用法也不仅仅如此,akka内部的设计构造也是非常复杂的,更详细的资料可以查询官网文档https://doc.akka.io/docs。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值