Akka-Cluster(0)- 分布式应用开发的一些想法
当我初接触akka-cluster的时候,我有一个梦想,希望能充分利用actor自由分布、独立运行的特性实现某种分布式程序。这种程序的计算任务可以进行人为的分割后再把细分的任务分派给分布在多个服务器上的actor上去运算。这些服务器都处于同一集群环境里,它们都是akka-cluster中的节点(node)。akka-cluster的节点数量只需要通过系统配置方式按照计算能力要求随意增减,在集群上运行的分布式程序可以在不修改软件的情况下自动调整actors在各节点上的分布,重新平衡程序运算负载,不受任何影响继续运行。
在前面akka系列的博客里也介绍了一些akka-cluster的情况,最近在“集群环境内编程模式(PICE)”的专题系列里又讨论了如何在集群环境里通过protobuf-gRPC把多个不同类型的数据库服务集成起来。因为集群中的数据库服务是用akka-stream连接的,我们把程序与数据一起作为stream的流元素用Flow发送给相应的数据库服务进行处理。这时一个想法就产生了:当数据库服务接收了一项服务要求后(假设数据处理多是耗时、耗资源的任务)可以对任务进行分割,然后把这些小任务再分发给所属集群内的多个节点上去运算,再按计算要求收集,汇总结果。那么如果能按用户数量和运算任务的规模来任意添减服务器数量就能满足任何规模的运算需求了。最重要的是这种集群节点规模调整必须是某种配置方式,即通过修改配置文件,但不需要修改软件代码。这些需要恰恰又是akka-cluster的特殊能力。所以决定开个akka-cluster的专题系列来具体讨论集群环境下的分布式软件开发模式。
akka-cluster提供的以下几种方式比较符合我们的要求:
1、distributed pub/sub - 分布式发布订阅模式
2、cluster-singleton - 单例actor模式
3、cluster-load-balancing - 集群负载均衡模式
4、cluster-sharding - 集群分片模式
在这个系列下面的博客里我们会逐个模式讨论它们在具体编程的使用细节。但首先探讨一下如何通过配置文件来定义akka-cluster节点,实现集群规模调整。
集群节点(cluster node)的生命周期会经历以下阶段:
Joining->Up,Leaving->Exiting,Exiting->Removed,Unreachable->Up,Unreachable->Down,Down->Removed
下面我们就用运行在不同集群节点的actor,通过订阅系统的集群成员状态转换消息来观察每个节点的状态转变:
class EventListener extends Actor with ActorLogging {
import EventListner._
val cluster = Cluster(context.system)
override def preStart(): Unit = {
cluster.subscribe(subscriber = self,initialStateMode = InitialStateAsEvents
,classOf[MemberEvent],classOf[UnreachableMember])
super.preStart()
}
override def postStop(): Unit = {
cluster.unsubscribe(self)
super.postStop()
}
override def receive: Receive = {
case MemberJoined(member) =>
log.info("{} is JOINING...", member.address)
case MemberUp(member) =>
log.info("{} is UP!", member.address)
case MemberWeaklyUp(member) =>
log.info("{} is weakly UP!", member.address)
case MemberLeft(member) =>
log.info("{} is LEAVING...", member.address)
case MemberExited(member) =>
log.info("{} is EXITING...", member.address)
case MemberRemoved(member, prevStatus) =>
log.info("{} is REMOVED! from state {}", member.address, prevStatus)
case UnreachableMember(member) =>
log.info("{} is UNREACHABLE!", member.address)
case ReachableMember(member) =>
log.info("{} is REACHABLE!", member.address)
case UnreachableDataCenter(datacenter) =>
log.info("Data Center {} is UNREACHABLE!", datacenter)
case ReachableDataCenter(datacenter) =>
log.info("Data Center {} is REACHABLE!", datacenter)
case Leave =>
cluster.leave(cluster.selfAddress)
log.info("{} is asked to leave cluster.",cluster.selfAddress)
case Down =>
cluster.down(cluster.selfAddress)
log.info("{} is asked to shutdown cluster.",cluster.selfAddress)
}
}
Leave和Down是自定义消息类型:
object EventListner {
trait Messages {}
case object Leave extends Messages
case object Down extends Messages
def props = Props(new EventListener)
...
}
akka-cluster最基本的配置文件内容如下:
akka {
actor {
provider = "cluster"
}
remote {
log-remote-lifecycle-events = off
netty.tcp {
hostname = "localhost"
port =
}
}
cluster {
seed-nodes = [
"akka.tcp://ClusterSystem@localhost:2551"]
}
}
实际上hostname,port,seed-nodes这些参数都可以在程序里配置,如果有需要,我们只要在配置文件里注明这是一个集群模式的程序就行了,其它参数放到程序里去定义:
akka {
actor {
provider = "cluster"
}
}
然后我们可以在程序里配置缺失的集群参数:
object EventListner {
trait Messages {}
case object Leave extends Messages
case object Down extends Messages
def props = Props(new EventListener)
def create(host: String = "localhost", port: Int = , seednode: String = "") = {
var config = ConfigFactory.parseString(s"akka.remote.netty.tcp.hostname=${host}")
.withFallback(ConfigFactory.parseString(s"akka.remote.netty.tcp.port=${port}"))
if (seednode.length > ) {
val strConfig = "akka.cluster.seed-nodes=[\"" + seednode + "\"]"
val configSeed = ConfigFactory.parseString(strConfig)
config = config.withFallback(configSeed)
}
config = config.withFallback(ConfigFactory.load("akka-cluster-config"))
val clusterSystem = ActorSystem(name="ClusterSystem",config=config)
clusterSystem.actorOf(Props[EventListener])
}
}
在create函数里ConfigFactory.parseString可以把一个字符串转换成集群配置参数,多个参数可以用withFallback来补充定义。
以下是EventListener的测试程序:
import EventListner._
object EventDemo extends App { val listner1 = EventListner.create(port = ) //seed node
scala.io.StdIn.readLine()
val listner2 = EventListner.create() //port=0 random port
scala.io.StdIn.readLine()
val listner3 = EventListner.create() //port=0 random port scala.io.StdIn.readLine() listner3 ! Leave
scala.io.StdIn.readLine() listner2 ! Down
scala.io.StdIn.readLine() listner1 ! Leave
scala.io.StdIn.readLine() }
第一个运行的必须是seednode,因为每个节点在启动时都需要连接seednode。下面是每个阶段的输出结果:
[INFO] [// ::40.888] [main] [akka.cluster.Cluster(akka://ClusterSystem)] Cluster Node [akka.tcp://ClusterSystem@localhost:2551] - Started up successfully
[INFO] [// ::40.931] [ClusterSystem-akka.actor.default-dispatcher-] [akka.cluster.Cluster(akka://ClusterSystem)] Cluster Node [akka.tcp://ClusterSystem@localhost:2551] - Node [akka.tcp://ClusterSystem@localhost:2551] is JOINING itself (with roles [dc-default]) and forming new cluster
[INFO] [// ::40.933] [ClusterSystem-akka.actor.default-dispatcher-] [akka.cluster.Cluster(akka://ClusterSystem)] Cluster Node [akka.tcp://ClusterSystem@localhost:2551] - Cluster Node [akka.tcp://ClusterSystem@localhost:2551] dc [default] is the new leader
[INFO] [// ::40.943] [ClusterSystem-akka.actor.default-dispatcher-] [akka.cluster.Cluster(akka://ClusterSystem)] Cluster Node [akka.tcp://ClusterSystem@localhost:2551] - Leader is moving node [akka.tcp://ClusterSystem@localhost:2551] to [Up]
[INFO] [// ::41.037] [ClusterSystem-akka.actor.default-dispatcher-] [akka.tcp://ClusterSystem@localhost:2551/user/$a] akka.tcp://ClusterSystem@localhost:2551 is UP!
[INFO] [// ::47.363] [ClusterSystem-akka.actor.default-dispatcher-] [akka.tcp://ClusterSystem@localhost:51679/user/$a] akka.tcp://ClusterSystem@localhost:51679 is JOINING...
[INFO] [// ::47.930] [ClusterSystem-akka.actor.default-dispatcher-] [akka.cluster.Cluster(akka://ClusterSystem)] Cluster Node [akka.tcp://ClusterSystem@localhost:2551] - Leader is moving node [akka.tcp://ClusterSystem@localhost:51679] to [Up]
[INFO] [// ::47.931] [ClusterSystem-akka.actor.default-dispatcher-] [akka.tcp://ClusterSystem@localhost:2551/user/$a] akka.tcp://ClusterSystem@localhost:51679 is UP!
[INFO] [// ::48.109] [ClusterSystem-akka.actor.default-dispatcher-] [akka.tcp://ClusterSystem@localhost:51679/user/$a] akka.tcp://ClusterSystem@localhost:51679 is UP!
[INFO] [// ::53.765] [ClusterSystem-akka.actor.default-dispatcher-] [akka.tcp://ClusterSystem@localhost:51681/user/$a] akka.tcp://ClusterSystem@localhost:51681 is JOINING...
[INFO] [// ::53.930] [ClusterSystem-akka.actor.default-dispatcher-] [akka.tcp://ClusterSystem@localhost:51679/user/$a] akka.tcp://ClusterSystem@localhost:51681 is JOINING...
[INFO] [// ::54.929] [ClusterSystem-akka.actor.default-dispatcher-] [akka.cluster.Cluster(akka://ClusterSystem)] Cluster Node [akka.tcp://ClusterSystem@localhost:2551] - Leader is moving node [akka.tcp://ClusterSystem@localhost:51681] to [Up]
[INFO] [// ::54.929] [ClusterSystem-akka.actor.default-dispatcher-] [akka.tcp://ClusterSystem@localhost:2551/user/$a] akka.tcp://ClusterSystem@localhost:51681 is UP!
[INFO] [// ::00.806] [ClusterSystem-akka.actor.default-dispatcher-] [akka.tcp://ClusterSystem@localhost:51681/user/$a] akka.tcp://ClusterSystem@localhost:51681 is asked to leave cluster.
[INFO] [// ::00.807] [ClusterSystem-akka.actor.default-dispatcher-] [akka.cluster.Cluster(akka://ClusterSystem)] Cluster Node [akka.tcp://ClusterSystem@localhost:51681] - Marked address [akka.tcp://ClusterSystem@localhost:51681] as [Leaving]
[INFO] [// ::00.808] [ClusterSystem-akka.actor.default-dispatcher-] [akka.tcp://ClusterSystem@localhost:51681/user/$a] akka.tcp://ClusterSystem@localhost:51681 is LEAVING...
[INFO] [// ::00.809] [ClusterSystem-akka.actor.default-dispatcher-] [akka.tcp://ClusterSystem@localhost:51679/user/$a] akka.tcp://ClusterSystem@localhost:51679 is asked to shutdown cluster.
[INFO] [// ::00.809] [ClusterSystem-akka.actor.default-dispatcher-] [akka.cluster.Cluster(akka://ClusterSystem)] Cluster Node [akka.tcp://ClusterSystem@localhost:51679] - Marking node [akka.tcp://ClusterSystem@localhost:51679] as [Down]
[INFO] [// ::00.810] [ClusterSystem-akka.actor.default-dispatcher-] [akka.tcp://ClusterSystem@localhost:2551/user/$a] akka.tcp://ClusterSystem@localhost:51681 is LEAVING...
[INFO] [// ::00.933] [ClusterSystem-akka.actor.default-dispatcher-] [akka.tcp://ClusterSystem@localhost:51679/user/$a] akka.tcp://ClusterSystem@localhost:51681 is LEAVING...
[INFO] [// ::01.101] [ClusterSystem-akka.actor.default-dispatcher-] [akka.cluster.Cluster(akka://ClusterSystem)] Cluster Node [akka.tcp://ClusterSystem@localhost:51679] - Shutting down myself
[INFO] [// ::01.102] [ClusterSystem-akka.actor.default-dispatcher-] [akka.cluster.Cluster(akka://ClusterSystem)] Cluster Node [akka.tcp://ClusterSystem@localhost:51679] - Shutting down...
[INFO] [// ::01.104] [ClusterSystem-akka.actor.default-dispatcher-] [akka.cluster.Cluster(akka://ClusterSystem)] Cluster Node [akka.tcp://ClusterSystem@localhost:51679] - Successfully shut down
[INFO] [// ::01.110] [ClusterSystem-akka.actor.default-dispatcher-] [akka.tcp://ClusterSystem@localhost:51679/user/$a] akka.tcp://ClusterSystem@localhost:2551 is REMOVED! from state Up
[INFO] [// ::01.110] [ClusterSystem-akka.actor.default-dispatcher-] [akka.tcp://ClusterSystem@localhost:51679/user/$a] akka.tcp://ClusterSystem@localhost:51679 is REMOVED! from state Down
[INFO] [// ::01.111] [ClusterSystem-akka.actor.default-dispatcher-] [akka.tcp://ClusterSystem@localhost:51679/user/$a] akka.tcp://ClusterSystem@localhost:51681 is REMOVED! from state Leaving
[INFO] [// ::02.925] [ClusterSystem-akka.actor.default-dispatcher-] [akka.cluster.Cluster(akka://ClusterSystem)] Cluster Node [akka.tcp://ClusterSystem@localhost:2551] - Leader is moving node [akka.tcp://ClusterSystem@localhost:51681] to [Exiting]
[INFO] [// ::02.926] [ClusterSystem-akka.actor.default-dispatcher-] [akka.tcp://ClusterSystem@localhost:2551/user/$a] akka.tcp://ClusterSystem@localhost:51681 is EXITING...
[INFO] [// ::02.927] [ClusterSystem-akka.actor.default-dispatcher-] [akka.cluster.Cluster(akka://ClusterSystem)] Cluster Node [akka.tcp://ClusterSystem@localhost:51681] - Exiting, starting coordinated shutdown
[INFO] [// ::02.927] [ClusterSystem-akka.actor.default-dispatcher-] [akka.tcp://ClusterSystem@localhost:51681/user/$a] akka.tcp://ClusterSystem@localhost:51681 is EXITING...
[INFO] [// ::02.934] [ClusterSystem-akka.actor.default-dispatcher-] [akka.cluster.Cluster(akka://ClusterSystem)] Cluster Node [akka.tcp://ClusterSystem@localhost:51681] - Exiting completed
Akka-Cluster(0)- 分布式应用开发的一些想法的更多相关文章
- Scala 的 Web 框架 Lift 开始 3.0 版本开发
Scala 的 Web 框架 Lift 开始 3.0 版本开发 http://demo.liftweb.net/ http://liftweb.net/download Lift 框架在不断的成长和改 ...
- Akka Cluster简介与基本环境搭建
akka集群是高容错.去中心化.不存在单点故障以及不存在单点瓶颈的集群.它使用gossip协议通信以及具备故障自动检测功能. Gossip收敛 集群中每一个节点被其他节点监督(默认的最大数量为 ...
- 《深入实践Spring Boot》阅读笔记之二:分布式应用开发
上篇文章总结了<深入实践Spring Boot>的第一部分,这篇文章介绍第二部分:分布式应用开发,以及怎么构建一个高性能的服务平台. 主要从以下几个方面总结: Spring Boot SS ...
- akka cluster 初体验
cluster 配置 akka { actor { provider = "akka.cluster.ClusterActorRefProvider" } remote { log ...
- asp.net 分布式应用开发
Net Framework推出的许多新技术为上述任务的实现提供了相对简单的解决方案.其中,基于SOAP的Web Service在处理分布式应用时具有比传统的DCOM/CORBA明显的优点,结合基于We ...
- 高性能分布式应用开发中间件ICE介绍
作为一个技术人员,你是否在为不断增长的数据量和日益复杂的业务逻辑而头疼不已,杂乱堆砌在一起的庞大业务让系统越来越脆弱,于是你想到了网格,想到了利用分布式来重组一个健壮的系统架构. 随后,RMI,EJB ...
- Akka系列(十):Akka集群之Akka Cluster
前言........... 上一篇文章我们讲了Akka Remote,理解了Akka中的远程通信,其实Akka Cluster可以看成Akka Remote的扩展,由原来的两点变成由多点组成的通信网络 ...
- akka cluster sharding source code 学习 (1/5) 替身模式
为了使一个项目支持集群,自己学习使用了 akka cluster 并在项目中实施了,从此,生活就变得有些痛苦.再配上 apache 做反向代理和负载均衡,debug 起来不要太酸爽.直到现在,我还对 ...
- 关于Quartus II 13.0对应开发NIOS II软件程序时报错Symbol 'NULL' could not be resolved问题的解决方法
关于Quartus II 13.0对应开发NIOS II软件程序时报错Symbol 'NULL' could not be resolved问题的解决方法 近期在评估使用NIOS II处理器进行项目的 ...
随机推荐
- Redis内存模型总结
一.Redis内存统计 在客户端通过redis-cli连接服务器后,通过info命令可以查看内存使用情况: info memory 返回结果中比较重要的几个说明如下: (1)used_memory:R ...
- python_05 可变类型与不可变类型、集合、字符串格式化
可变数据类型与不可变数据类型: 1.可变:列表,字典 2.不可变:字符串,数字,元组 访问顺序: 1.顺序访问:字符串,列表,元组 2.映射:字典 集合 由不同元素组成的集合,集合中是一组无序排列的可 ...
- Python模块定义和使用
Python中所谓的模块就是一个Python文件,一个abc.py的文件就是一个名字叫abc的模块,一个xyz.py的文件就是一个名字叫xyz的模块.模块由代码.函数或类组成.编程中使用模块不仅可以提 ...
- JS event loop
一.为什么JavaScript是单线程? JavaScript语言的一大特点就是单线程,也就是说,同一个时间只能做一件事.那么,为什么JavaScript不能有多个线程呢?这样能提高效率啊. Java ...
- Mad Lids游戏 华氏与摄氏温度转换
name1 = input('请输入一个名字:') name2 = input('请输入一个名字:') vehicle = input('请输入一种车子:') print('\n上近代史的{}刚下课, ...
- 【python中二叉树的实现】python中二叉树的创建、三种方式递归遍历和非递归遍历
代码如下: # coding=utf-8 class myNode(object): def __init__(self, data=-1, lchild=None, rchild=None): se ...
- 加NONCLUSTERED INDEX索引,在ON了之后还要INCLUDE
之前加了索引,但效果不大 SET STATISTICS TIME ON --执行时间 SET STATISTICS IO ON --IO读取 DBCC DROPCLEANBUFFERS --清除缓冲区 ...
- Aria2+WebUI,迅雷倒下之后的代替品
Aria2+WebUI,迅雷倒下之后的代替品 (2017-07-24 12:56:28) 转载▼ 分类: 软件 最近迅雷越来越作死了,砍第三方远程下载,强推迅雷9喂用户的屎,下载资源能砍就砍,以前 ...
- c++模板笔记
使用vc2015进行C++ 模板的学习实验和笔记 用简单示例学习了解STL template大部头理论书 讲解各种规则和各种规则例外的解决办法 集中精力在20%的规则中的阴暗角落而不是80%实践中要注 ...
- gitlab 10汉化
记得备份 先检查一下版本,好下载对应的汉化包 cat /opt/gitlab/embedded/service/gitlab-rails/VERSION 1)然后下载10.0.x.diff 文件到服务 ...