在学习akka过程中,我们了解了它的监督机制,会发现actor非常可靠,可以自动的恢复.但akka框架只会简单的创建新的actor,然后调用对应的生命周期函数,如果actor有状态需要回复,我们需要hook对应的生命周期函数,自己恢复状态.但此时恢复的只是初始状态,如果actor在接收消息过程中,状态发生了变化,为了保证可恢复就需要我们自行把状态保存在第三方组件了.考虑到通用性以及Actor模型的特点,akka提供了持久化机制,可以帮助我们做状态恢复. 其实,actor的状态千差万别,怎么来统一…
Akka Persistence Query是对akka持久化的一个补充,它提供了统一的.异步的流查询接口.今天我们就来研究下这个Persistence Query. 前面我们已经分析过Akka Persistence,它是用来持久化actor状态并在适当时机恢复actor的,简单来说它是用来写入的.那么Persistence Query与Persistence相对应,是用来读取数据的,一般用在读写分离的read side. Persistence Query主要的目标是设计一套松散的API,这…
对不起,akka typed 我是不准备进行源码分析的,首先这个库的API还没有release,所以会may change,也就意味着其概念和设计包括API都会修改,基本就没有再深入分析源码的意义了.另外这个typed actor跟OOP的class差别还是很大的,目前的API还是很丑的,至少我看着非常别扭啊.如果各位读者对这个有兴趣,去谷歌一下,看看它一些简单的介绍就好了,千万不要在生产使用. 不过我们也可以分析一下akka typed出现的原因.在我们会用actor开发的时候一定会遇到这样的…
今天我们来讲解akka-streams,这应该算akka框架下实现的一个很高级的工具.之前在学习akka streams的时候,我是觉得云里雾里的,感觉非常复杂,而且又难学,不过随着对akka源码的深入,才逐渐明白它到底是怎么一回事.下面介绍主要摘自akka官网,但会融入我的理解,以及部分源码,以减少大家学习的难度. 首先近几年流式计算很火,有各种各样的框架,比如spark.storm.flink等,当然前提是我们得有这样的需求.随着数据量越来越大,我们很难一次性处理全部的数据,只能采用流水线或…
一个应用软件维护的后期一定是要做监控,akka也不例外,它提供了集群模式下的度量扩展插件. 其实如果读者读过前面的系列文章的话,应该是能够自己写一个这样的监控工具的.简单来说就是创建一个actor,它负责收集节点的性能信息,然后用eventStream或者PUB/SUB把消息发布出去,需要这个信息的actor或者router订阅,然后根据信息做响应的操作就好了.当然了,akka估计也是这样做的,因为在akka里面一切都是actor. akka实现的Metrics扩展能够搜集系统性能指标,并能够把…
在ClusterClient源码分析中,我们知道,他是依托于“Distributed Publish Subscribe in Cluster”来实现消息的转发的,那本文就来分析一下Pub/Sub是如何实现的. 还记得之前分析Cluster源码的文章吗?其实Cluster只是把集群内各个节点的信息通过gossip协议公布出来,并把节点的信息分发出来.但各个actor的地址还是需要开发者自行获取或设计的,比如我要跟worker通信,那就需要知道这个actor在哪个节点,通过actorPath或ac…
akka Cluster基本实现原理已经分析过,其实它就是在remote基础上添加了gossip协议,同步各个节点信息,使集群内各节点能够识别.在Cluster中可能会有一个特殊的节点,叫做单例节点.也就是具有某个角色的节点在集群中只能有一个,如果这个节点宕机了,需要把这个角色的工作转移到其他节点. 使用单例模式有几个场景. 负责集群的一致性决策,跨集群协调工作.比如集群事务. 对外部系统的单实例接入. 一个master,多个worker 集中化的命名服务或路由策略. 官网说单例对象在设计时,永…
生命周期监控,也就是死亡监控,是akka编程中常用的机制.比如我们有了某个actor的ActorRef之后,希望在该actor死亡之后收到响应的消息,此时我们就可以使用watch函数达到这一目的. class WatchActor extends Actor { val child = context.actorOf(Props.empty, "child") context.watch(child) // <-- this is the only call needed for…
前面几篇博客,我们依次介绍了local和remote的一些内容,其实再分析cluster就会简单很多,后面关于cluster的源码分析,能够省略的地方,就不再贴源码而是一句话带过了,如果有不理解的地方,希望多翻翻之前的博客. 在使用cluster时,配置文件中的akka.actor.provider值是cluster,所以ActorSystem对应的provider就是akka.cluster.ClusterActorRefProvider. /** * INTERNAL API * * The…
使用过akka的应该都知道,默认情况下,消息是按照最多一次发送的,也就是tell函数会尽量把消息发送出去,如果发送失败,不会重发.但有些业务场景,消息的发送需要满足最少一次,也就是至少要成功发送一次.akka在Persistence的基础之上提供了at-least-once传递的语法. 简单来说akka中的at-least-once机制,会在规定时间内等待消息接收成功的确认消息.如果收到,则发送成功:否则,尝试重发:超过重试次数则不再重发. 其实如果不看akka的源码,让我们自己来实现至少一次的…
熔断器,在很多技术栈中都会出现的一种技术.它是在分布式系统中提供一个稳定的阻止嵌套失败的机制. 该怎么理解呢?简单来说,在分布式环境中,如果某个计算节点出现问题,很容易出现失败的逆向传到或整个系统的雪崩.什么意思呢?比如某个服务按照顺序依次调用了其他的三个服务,分别为A/B/C.如果B服务由于某种原因,响应变慢了,本来100毫秒就完成了,现在是1秒.此时A就会等待B服务的时间也就变成了1秒,那么就意味着会有很多的A服务调用在等待,如果并发量非常大,很容易就会造成A服务所在的节点出现问题,也就是说…
今天我们来谈一下akka的序列化框架,其实序列化.反序列化是一个老生常谈的问题,那么我们为什么还要研究一下akka的序列化框架呢?不就是使用哪种序列化.反序列化方法的区别么?其实刚开始的时候我也是这么想的,但是针对性.系统性的分析一下akka的序列化.反序列化过程,就会发现这个问题其实还是挺有意思的. 我们首先来看下什么是序列化.反序列化.序列化 (Serialization)将对象的状态信息转换为可以存储或传输的形式的过程:反序列化,就是通过从存储区中读取或反序列化对象的状态,重新创建该对象.…
上一篇博客中我们介绍了ActorMaterializer的一小部分源码,其实分析的还是非常简单的,只是初窥了Materializer最基本的初始化过程及其涉及的基本概念.我们知道在materialize过程中,对Graph进行了某种遍历,然后创建了actor,最终graph运行起来.那Graph相关的概念我们其实是没有进行深入研究的.但Graph定义又非常抽象,乍一看非常难于理解.但我在阅读官方文档的时候发现了自定义流处理过程的章节,这应该有助于我们理解Graph,此处对其做简要分析. Grap…
本博客逐步分析Akka Streams的源码,当然必须循序渐进,且估计会分很多篇,毕竟Akka Streams还是比较复杂的. implicit val system = ActorSystem("QuickStart") implicit val materializer = ActorMaterializer() 在使用Streams相关的API时,上面两个对象是必须创建的.ActorSystem不再说了,我们来看ActorMaterializer. /** * An ActorM…
上一篇博客我们研究了集群的分片源码,虽然akka的集群分片的初衷是用来解决actor分布的,但如果我们稍加改造就可以很轻松的开发出一个简单的分布式缓存系统,怎么做?哈哈很简单啊,实体actor的id就是key,actor的状态就是value,而且还可以无锁的改变状态. 其实akka的DistributedData有点类似缓存系统,当你需要在集群中分享数据的话,DistributedData就非常有用了.可以通过跟K/V缓存系统类似的API来存取数据,不过DistributedData中南的数据是…
个人觉得akka提供的cluster工具中,sharding是最吸引人的.当我们需要把actor分布在不同的节点上时,Cluster sharding非常有用.我们可以使用actor的逻辑标识符与actor进行通信,而不用关心其物理位置.简单来说就是把actor的actorPath或actorRef进一步抽象,用一个字符串表示. sharding可以表示为DDD领域中的聚合根,此时actor叫做“实体”.这些actor一般都具有持久化的状态.每个实体actor只运行在某个节点,我们可以在不知道a…
ClusterClient可以与某个集群通信,而本身节点不必是集群的一部分.它只需要知道一个或多个节点的位置作为联系节点.它会跟ClusterReceptionist 建立连接,来跟集群中的特定节点发送消息.而且必须把provider改成remote或cluster.receptionist需要在集群所有节点或一组节点内启动,它可以自行启动或通过ClusterReceptionist 扩展来启动.ClusterClient可以进行通信的actor必须是通过ClusterReceptionis扩展…
Actor故障恢复是akka中非常重要的内容,在之前的博客中虽然有介绍,但都是杂糅在其他知识点的细节中,本博客将单独介绍这一部分的故障恢复.为了简化分析的单独,本文只研究用户的actor故障恢复的步骤,系统actor也基本差不多:另外,为了简化篇幅,不重要的源码将不再整段贴出来,感兴趣的读者可以去下载源码单独分析. 上面是官方描述actor树形结构时用到的一个图,非常重要,希望读者一定要记住这个树形的层次概念.也就是说,actor一定会归属到某个父actor下面,而actor的故障恢复就是通过这…
akak中还有一个比较重要的概念,那就是Router(路由).路由的概念,相信大家都不陌生,在akka中,它就是其他actors的一个代理,会把消息按照路由规则,分发给指定的actor.我一般喜欢把Router用作负载均衡. 其实如果不看官方的源码或不使用官方Router,我们自己实现一个router也还是很简单的,因为一共有三个重要的概念:路由.路由策略.路由对象.路由负责接收消息,按照路由策略把消息传递给路由对象.在akka中,路由和路由对象都是一个普通的actor,只不过路由策略需要我们涉…
akka还有一个不常使用.但我觉得比较方便的一个模块,那就是FSM(有限状态机).我们知道了akka中Actor模型的具体实现之后,就会发现,Akka的actor可以非常方便的实现FSM.其实在akka中实现FSM还是非常简单的,或者说我们在akka中有意无意的都在使用FSM. 在介绍分析akka中FSM源码之前,先简单介绍下FSM基本概念.一个有限状态机是一个设备,或者是一个设备模型,具有有限数量的状态,它可以在任何给定的时间根据输入进行操作,使得一个状态变换到另一个状态,或者是使一个输入或者…
一个设计优秀的工具或框架,应该都有一个易用.强大的插件或扩展体系,akka也不例外. akka的扩展方法非常简单,因为只涉及到两个组件:Extension. ExtensionId.其中Extension在每个ActorSystem中只会加载一次,然后被akka管理.你可以在ActorSystem启动的时候以编程的方式加载,也可以通过配置的方式自动加载.由于Extension是在ActorSystem层面的扩展,所以需要开发者自己处理线程安全的问题.ExtensionId可以理解为Extensi…
在akka官网中关于远程actor交互,介绍了两种方法,一种是通过actorSelection查询,另一种是通过actorOf在远程节点创建一个actor.actorSelection我们之前的博客中已经介绍过,创建远程actor也有过简要说明,但其原理并没有做过多分析.下面就来分析一下如何在远程节点部署actor. 官网介绍了两种部署远程actor的方法,一种是通过akka.actor.deployment配置,另一种是编程,都比较简单.考虑到代码完整性,下面摘录akka官方样例库中的代码做分…
上一篇博客中,我们研究了remote模式下如何发消息给远程actor,其实无论如何,最终都是通过RemoteActorRef来发送消息的.另外官网也明确说明了,ActorRef是可以忽略网络位置的,这其实有两点含义:1.ActorRef可以序列化后跨网络传输:2.ActorRef反序列化后在本地可以正常识别是本地还是远程.那么实现位置透明就有两个关键点:1.ActorRef的序列化过程:2.ActorRef的识别.下面我们来逐一研究这两个关键点. 在local模式下,是通过InternalAct…
上一遍博客中,我们分析了网络链接建立的过程,一旦建立就可以正常的收发消息了.发送消息的细节不再分析,因为对于本地的actor来说这个过程相对简单,它只是创立链接然后给指定的netty网路服务发送消息就好了.接收消息就比较麻烦了,因为这对于actor来说是透明的,netty收到消息后如何把消息分发给指定的actor呢?这个分发的过程值得研究研究. 之前分析过,在监听创立的过程中,有一个对象非常关键:TcpServerHandler.它负责链接建立.消息收发等功能.TcpServerHandler继…
上一篇博客中,我们分析了Akka remote模式下消息发送的过程,但细心的读者一定发现没有介绍网络相关初始化.创建链接.释放链接的过程,本文就介绍一下相关的内容. 网络初始化就离不开ActorSystem的初始化,毕竟ActorSystem初始化之后就可以创建Actor并发送远程消息了.在ActorSystem初始化时,调用了RemoteActorRefProvider的init函数,init创建了Remoting这个RemoteTransport,并调用了start,而RemoteTrans…
上一篇博客我们介绍了remote模式下Actor的创建,其实与local的创建并没有太大区别,一般情况下还是使用LocalActorRef创建了Actor.那么发消息是否意味着也是相同的呢? 既然actorOf还是委托给了LocalActorRef,那么在本地创建的Actor发消息还是跟以前一样的,那么如果如何给远程的Actor发消息呢?我们一般是通过actorSelection或者给远程Actor发送一个Identify消息,来接收对应的ActorRef,然后再发消息.我们来分析一下这两者的区…
前面的文章都是基于local模式分析的,现在我们简要分析一下在remote模式下,ActorSystem的创建过程. final val ProviderClass: String = setup.get[BootstrapSetup] .flatMap(_.actorRefProvider).map(_.identifier) .getOrElse(getString("akka.actor.provider")) match { case "local" ⇒ c…
在我之前的博文中,已经介绍过要慎用Actor的ask.这里我们要分析一下ask的源码,看看它究竟是怎么实现的. 开发时,如果要使用ask方法,必须要引入akka.pattern._,这样才能使用ask(或者?)方法,那么想必ask是在akka.pattern._对应的包里面实现的. /* * Implementation class of the “ask” pattern enrichment of ActorRef */ final class AskableActorRef(val act…
分析源码的过程中我们发现,Akka出现了Actor.ActorRef.ActorCell.ActorContext等几个相似的概念,它们之间究竟有什么区别和联系呢? /** * Actor base trait that should be extended by or mixed to create an Actor with the semantics of the 'Actor Model': * <a href="http://en.wikipedia.org/wiki/Actor…
在上一遍博客中,我们已经分析了actor创建的大致过程,但只是涉及到了Dipatcher/Mailbox/ActorCell/InternalActorRef等对象的创建,并没有介绍我们自定义的继承Actor特质的类如何完成初始化.这篇文章对这一部分内容进行简单的补充.在akka.actor.dungeon.init代码中,有一段代码我们当时没有分析,此处对此代码进行深入分析,然后才能找到Actor子类完成创建的真实过程.上面是init的代码片段,其中有一个局部变量createMessage,根…