akka使用场景及原理简介
Akka显然不是银弹。如果Akka很完美,那现在在开发领域也没spring框架什么事了,甚至没Java的事了:Akka会凭一己之力把Scala推向JVM第一编程语言的宝座。而现在并没有:这也不是我希望的,毕竟我喜欢Java还是远超Scala。Akka由其特性,限定了它能用的主要场景有限(但不是很少)。这篇文章简介描述一下Akka的使用场景和基本原理。
Akka是基于actor思想的。actor认为任何独立的计算过程都应该分布在不同的时空里:要么是不同的actor,要么是同一个actor的不同时间。比如一个系统里面,计算员工的平均工资和计算用户的移动轨迹就是两个独立无关的过程,它们就应该使用不同的actor;而对于一个大规模的计算,为了充分利用多CPU(甚至多物理机)可以将计算过程拆分成多个独立的计算并分给多个actor,最终汇总它们的结果(这不就是MapReduce?毕竟计算的思路是想通的)。
纯粹使用actor的系统称为使用了actor模型。注意,使用actor模型的系统不能使用其他模型,只能包含actor。显而易见,使用spring框架的系统哪怕集成了Akka或其他Actor实现也一定不能称为用到了actor模型,因为毕竟里面有大量的非actor bean对象。actor模型的宗旨是“一切都是actor”。
第一个可以使用actor模型的场景(不是可以,其实是应该)是对高并发有严格要求,并且需要维护某种状态。actor最强大之处就是它的并发处理能力,开箱即用就能写出无竞态逻辑的代码。因为actor之间只能通过消息传递信息,消息在Actor模型中是不可变的(在Java中的表现就是消息是final
的变量);另外同一个actor处理不同消息时是单线程的,只能一条一条处理(actor模型中消息的处理是无序的,但Akka提供了顺序支持,消息在Akka中只能按照其被送到信箱的先后顺序被处理)。
另一个应该选择actor的情况是构建有限状态机FSM。actor的结构自然而然可以轻松应对这种场景。
其他可以使用actor的场景可以随着你对它理解的逐步加深进行进一步判断。不过上面两种场景覆盖的业务面也很广阔了,毕竟现实开发中有高并发或需要状态机是常有的事情。这样有点类似8020的应用:在对actor理解尚不充分的情况下帮你解决了大部分困难场景。
目前比较常见的集成实践有两种:一种是只对顶层实体进行actor建模,而在actor内部使用面向对象或函数式编程;另一种相反,在接口的实现中使用actor,目的是不暴露actor给接口的调用方,调用者完全不知道服务中存在actor。两种方式都符合领域模型。
Akka使用gossip —— 流行病协议 ——进行通信,所以是去中心化的(Consul也使用的这种协议)。gossip的通信效率不高,在网络拓扑发生变化的时候,需要一定的时间才能最终完成同步。当然不同的gossip协议实现带来的同步效率也不同。
去中心化的好处是不需要一个中心节点,如果有中心节点它可能会成为性能瓶颈,而且为了HA还得为中心节点搭建集群。Akka中需要指定种子节点,非种子节点只能加入种子节点所在的集群。不过你可以指定所有节点都是种子节点。种子节点会自动产生一个leader,但是这个leader能做的工作很少,只能向所在集群增加或移除节点。所以没有leader集群一般也能正常工作:这就是和中心化中leader的区别。
去中心化会带来一个业界称为“脑裂”的问题:同一套集群中的一些机器和另一些机器因为网络问题不能通行,两组机器就会自动形成两个集群,这显然不是我们能接受的。
所以线上不要打开自动下线,机器不能访问也不能将其从集群移除。如果必须开启自动下线,可以配置形成集群的最少机器数:达不到阈值就形不成集群,但这样不能完全规避风险。万一就是出现大量机器同时下线呢,比如它们在同一个机房
Akka中访问远程actor和同虚拟机actor没有什么差别,熟悉dubbo的同学很熟悉这一点用法。Akka对本地调用进行了优化,当发现消息发给的是本地actor的时候,不会像发给远程那样进行序列化和请求网络。
