第1章 初识Actor
概述
Actor是一种并发计算的理论模型,akka是Actor模型的一种实现,通常是指一个分布式工具集,用于协调远程计算资源来进行一些工作。
Actor模型的起源
Actor模型来源于1973年发表的名为《A Universal Modular Acotr Formalism For Atrifical Intelligence》论文,该论文提出了并发计算的理论模型。在Actor模型中,actor是一个并发原语。
Actor模型的一些概念
- Actor:表示一个节点的并发原语,同步处理接收到的消息。
- 消息:actor之间通信的数据(数据载体)。
- 消息传递:一种软件开发范式,通过传递消息来触发各种行为,包括状态的变更。
- 邮箱地址:消息传递的目标地址。
- 邮箱:存储消息的仓库,或者称之为消息队列。
- Actor系统:多个Actor的集合。
好处:
- 使得并发事件分析变得清晰。
- 消除共享状态。
第2章 Actor与并发
响应式系统设计
Akka也被称作一种响应式平台,更具体的说,是typesafe响应式平台的一部分。响应式宣言四准则:灵敏性、伸缩性、容错性、事件驱动/消息驱动。
剖析actor
package com.example
import akka.actor.{Actor, ActorLogging, ActorSystem, Props, Status}
object PongActor {
def props(resp:String):Props = Props.create(classOf[PongActor],resp)
}
class PongActor(val msg:String) extends Actor with ActorLogging{
override def receive: Receive = {
case "Ping" => {
log.info(s"receive ping from (${sender()})")
}
case _ => sender() ! Status.Failure(new Exception("Unknown Message"))
}
}
object AkkaPingPong extends App {
val system = ActorSystem.create("pong")
val pongActor1 = system.actorOf(PongActor.props("pong"),"hello")
val ponpActor2 = system.actorSelection("akka://pong/user/hello")
ponpActor2 ! "Ping"
pongActor1 ! "Ping"
pongActor1 ! "Pong"
}
actor路径
- akka://ActorSystemName/user/HelloWorldActor
- akka.tcp://ActorSystemName@remote-host:port/user/HelloWorldActor
注意: 该章节有个K-V数据库的实例,看完所有章节后可以代码实现一下。
第3章 传递消息
消息传递模式
- Tell
- Ask
- Forward
- Pipe
Ask消息模式
在调用ask向Actor发起请求时,ask会先返回一个Future作为结果第占位符,同事会创建一个临时Actor,sender()函数指向的即为这个临时Actor的引用,当计算技术,临时Actor会使用返回的相应来完成Future,注意,这种模式下,返回消息并不直接传递给sender邮箱。该模式下,需要设置超时时间。注意Future回调函数的执行是在另一个线程上。
Tell消息模式
tell是一种称为fire and forgetd的非阻塞的调用方式,tell之后,消息只是被放在receiver的邮箱中,该模式需要填充sender,如果不填充,不设置任何默认邮箱(DeadLetters),
Forward消息模式
当一个actor接受到一个消息时,将该消息转发给另一个actor,sender()指向的还是原始的发送者而非当前转发者,除此之外,和tell模式并无区别。
Pipe消息模式
该模式旨在将Future转发给sender()或者另一个Actor。该模式产生的背景在于Future中如果需要使用Sender,需要临时存储sender()的值,原因在于sender()返回结果跟上下文有关,而Future回调函数的执行是在另一个线程中,故而产生pipe这种模式。
第4章 Actor的生命周期
分布式计算的8个误区
- 网络是可靠的
- 没有延迟
- 带宽是无限的
- 网络是安全的
- 网络拓扑不会改变
- 只有一个管理员
- 网络传输没有开销
- 网络是同构的
监督
Akka借鉴了ErLang的监督机制,采用层级监督,每个Father监督所有的Children,顶层的Actor节点为/,actorOf创建的actor都在/user下面,监督事件都发生在/system下的actor。
监督策略:
- 继续(resume):Actor继续处理下一条消息。
- 停止(stop):停止Actor,不再做任何操作。
- 重启(restart):新建一个Actor,代替原来的Actor。
- 向上反应(esclate):将异常消息传递给上一个监督者。
默认监督策略
- Actor运行过程中发生异常:reatart
- Actor运行过程中发生错误:esclate
- Actor初始化过程中发生异常:stop
- ActorKilledException:stop
Actor生命周期
preRestart和postRestart只在重启的时候会调用,preRestart默认会调用postStart,postRestart默认调用preStart。
终止或kill一个Actor
- ActorSystem.stop(actorRef)
- ActorContext.stop(actorRef)
- 给Actor发送一条PosionKill消息
- 给Actor发送一条Kill消息
调用context.stop或system.stop会导致Actor立即停止;发送PosionKill消息会在Actor处理完给消息后才停止,和普通消息并没有区别;kill并不会马上停止Actor,会导致Actor抛出一个ActorKillException异常,由监督机制来决定。
生命周期监控
Actor可以对其他任何Actor进行监控,采用context.watch(actorRef)即可做到这一点,调用context.unwatch(actorRerf)可以取消监控,当被监控Actor终止时,负责监控当Actor会收到一条Terminated(actorRef)消息。
状态
改变Actor行为的机制
- 基于Actor状态的条件语句
- 热交换(become/unbecome)
- 有限状态机
注意:akka提供stash操作,如果当前状态不可用,可以采用stash,将某些消息放在一个独立的队列中,消息该队列一处,当状态可用是,采用unstash操作,将独立队列中的消息重新放回邮箱。
第5章 纵向扩展
akka提供了两种多核并发编程的抽象:Future和Actor。
Router
Router是一个用于负载均衡和路由的抽象,分为Group和Pool两种。
路由逻辑
dispatcher
Dispatcher解析
Dispatcher基于Executor,将如何执行任务和何时执行任务解耦,一般来说,dispatcher会包含一些线程,这些线程负责调度并运行任务,比如处理Actor的消息以及线程中的Future事件。
Executor
- ThreadPool Executor:有一个工作队列,包含了要分配给各线程的任务,允许线程重用,减少了线程创建与销毁的开销。
- ForkJoinPool Executor:使用一种分治算法,递归的将任务分解成一系列子任务,由各线程执行子任务,并合并结果。因为某些任务无法被递归分解成子任务,因此又提供了一种窃取算法,允许某些空闲线程窃取另一个线程的工作。一般情况下,该方案比ThreadPool效率要高。
dispatcher分类
- Dispatcher:默认的Dispatcher,将使用定义的Executor。
- PinnedDispatcher:给每个Actor都分配自己独有的线程,每个Actor一个ThreadPoolExecutor,该线程池只有一个线程,一般用于需要处理很多重要的工作场景。
- CallingThreadDispatcher:没有Executor,在发起调用的线程上执行,一般用于测试。
- BalancingPoolDispatcher:Pool中的所有Actor都共享一个邮箱,该方式目前已不推荐使用。
第6章 横向扩展
集群
是一组松散或紧密连接在一起工作的计算机。由于这些计算机协同工作,在许多方面它们可以被视为单个系统。 --维基百科
失败检测
akka通过在节点建发送心跳消息并接受相应来进行失败检测,节点只和相邻的几个节点发送心跳消息。当发现事故后,通过gossip协议进行传播,以达到最终一致性。
CAP
- Consistency
- Availability
- Partition Tolerance
CP系统-一致性优先
Redis和具有冗余备份当RDBMS都属于这种。
AP系统-可用性优先
Cassandra和Riak属于这种。
使用Akka Cluster构建系统
配置
种子节点
当一个节点想要加入集群时,需要先连接种子节点,由种子节点通过gossip协议将其地址发布到整个集群,如果第一个连接失败,尝试连接第二个,一般而言,最少需要两个种子节点。
集群成员状态
- joining(leader将其标记为up)
- up
- leaving(leader将其标记为removed)
- removed
- memberUnreachable(leader将其标记为down)
- down(只能重启,重新加入集群)
第7章 邮箱
熔断
熔断机制会监控应用程序某些部分的响应延迟或者错误。消息会首先发送给熔断器,熔断器会监控相应时间,最初状态为关闭,如果监控到响应时间超过上线,则熔断器状态改为开启。
熔断器处于开启状态,会拒绝所有收到到请求。
一段时间后,熔断器会将状态改为半开,尝试者接受请求,如果很快能得到相应,则认为已经恢复,状态改为关闭。
思维导图
阅读文档:《akka入门与实践》