当我初接触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,通过订阅系统的集群成员状态转换消息来观察每个节点的状态转变:

  1. class EventListener extends Actor with ActorLogging {
  2. import EventListner._
  3.  
  4. val cluster = Cluster(context.system)
  5.  
  6. override def preStart(): Unit = {
  7. cluster.subscribe(subscriber = self,initialStateMode = InitialStateAsEvents
  8. ,classOf[MemberEvent],classOf[UnreachableMember])
  9. super.preStart()
  10. }
  11. override def postStop(): Unit = {
  12. cluster.unsubscribe(self)
  13. super.postStop()
  14. }
  15.  
  16. override def receive: Receive = {
  17. case MemberJoined(member) =>
  18. log.info("{} is JOINING...", member.address)
  19. case MemberUp(member) =>
  20. log.info("{} is UP!", member.address)
  21. case MemberWeaklyUp(member) =>
  22. log.info("{} is weakly UP!", member.address)
  23. case MemberLeft(member) =>
  24. log.info("{} is LEAVING...", member.address)
  25. case MemberExited(member) =>
  26. log.info("{} is EXITING...", member.address)
  27. case MemberRemoved(member, prevStatus) =>
  28. log.info("{} is REMOVED! from state {}", member.address, prevStatus)
  29. case UnreachableMember(member) =>
  30. log.info("{} is UNREACHABLE!", member.address)
  31. case ReachableMember(member) =>
  32. log.info("{} is REACHABLE!", member.address)
  33. case UnreachableDataCenter(datacenter) =>
  34. log.info("Data Center {} is UNREACHABLE!", datacenter)
  35. case ReachableDataCenter(datacenter) =>
  36. log.info("Data Center {} is REACHABLE!", datacenter)
  37. case Leave =>
  38. cluster.leave(cluster.selfAddress)
  39. log.info("{} is asked to leave cluster.",cluster.selfAddress)
  40. case Down =>
  41. cluster.down(cluster.selfAddress)
  42. log.info("{} is asked to shutdown cluster.",cluster.selfAddress)
  43. }
  44.  
  45. }

Leave和Down是自定义消息类型:

  1. object EventListner {
  2. trait Messages {}
  3. case object Leave extends Messages
  4. case object Down extends Messages
  5. def props = Props(new EventListener)
  6. ...
  7. }

akka-cluster最基本的配置文件内容如下:

  1. akka {
  2. actor {
  3. provider = "cluster"
  4. }
  5. remote {
  6. log-remote-lifecycle-events = off
  7. netty.tcp {
  8. hostname = "localhost"
  9. port =
  10. }
  11. }
  12. cluster {
  13. seed-nodes = [
  14. "akka.tcp://ClusterSystem@localhost:2551"]
  15. }
  16. }

实际上hostname,port,seed-nodes这些参数都可以在程序里配置,如果有需要,我们只要在配置文件里注明这是一个集群模式的程序就行了,其它参数放到程序里去定义:

  1. akka {
  2. actor {
  3. provider = "cluster"
  4. }
  5. }

然后我们可以在程序里配置缺失的集群参数:

  1. object EventListner {
  2. trait Messages {}
  3. case object Leave extends Messages
  4. case object Down extends Messages
  5. def props = Props(new EventListener)
  6.  
  7. def create(host: String = "localhost", port: Int = , seednode: String = "") = {
  8. var config = ConfigFactory.parseString(s"akka.remote.netty.tcp.hostname=${host}")
  9. .withFallback(ConfigFactory.parseString(s"akka.remote.netty.tcp.port=${port}"))
  10. if (seednode.length > ) {
  11. val strConfig = "akka.cluster.seed-nodes=[\"" + seednode + "\"]"
  12. val configSeed = ConfigFactory.parseString(strConfig)
  13. config = config.withFallback(configSeed)
  14. }
  15. config = config.withFallback(ConfigFactory.load("akka-cluster-config"))
  16. val clusterSystem = ActorSystem(name="ClusterSystem",config=config)
  17. clusterSystem.actorOf(Props[EventListener])
  18. }
  19.  
  20. }

在create函数里ConfigFactory.parseString可以把一个字符串转换成集群配置参数,多个参数可以用withFallback来补充定义。

以下是EventListener的测试程序:

  1. import EventListner._
  2. object EventDemo extends App {
  3.  
  4. val listner1 = EventListner.create(port = ) //seed node
  5. scala.io.StdIn.readLine()
  6. val listner2 = EventListner.create() //port=0 random port
  7. scala.io.StdIn.readLine()
  8. val listner3 = EventListner.create() //port=0 random port
  9.  
  10. scala.io.StdIn.readLine()
  11.  
  12. listner3 ! Leave
  13. scala.io.StdIn.readLine()
  14.  
  15. listner2 ! Down
  16. scala.io.StdIn.readLine()
  17.  
  18. listner1 ! Leave
  19. scala.io.StdIn.readLine()
  20.  
  21. }

第一个运行的必须是seednode,因为每个节点在启动时都需要连接seednode。下面是每个阶段的输出结果:

  1. [INFO] [// ::40.888] [main] [akka.cluster.Cluster(akka://ClusterSystem)] Cluster Node [akka.tcp://ClusterSystem@localhost:2551] - Started up successfully
  2. [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
  3. [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
  4. [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]
  5. [INFO] [// ::41.037] [ClusterSystem-akka.actor.default-dispatcher-] [akka.tcp://ClusterSystem@localhost:2551/user/$a] akka.tcp://ClusterSystem@localhost:2551 is UP!
  1. [INFO] [// ::47.363] [ClusterSystem-akka.actor.default-dispatcher-] [akka.tcp://ClusterSystem@localhost:51679/user/$a] akka.tcp://ClusterSystem@localhost:51679 is JOINING...
  2. [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]
  3. [INFO] [// ::47.931] [ClusterSystem-akka.actor.default-dispatcher-] [akka.tcp://ClusterSystem@localhost:2551/user/$a] akka.tcp://ClusterSystem@localhost:51679 is UP!
  4. [INFO] [// ::48.109] [ClusterSystem-akka.actor.default-dispatcher-] [akka.tcp://ClusterSystem@localhost:51679/user/$a] akka.tcp://ClusterSystem@localhost:51679 is UP!
  1. [INFO] [// ::53.765] [ClusterSystem-akka.actor.default-dispatcher-] [akka.tcp://ClusterSystem@localhost:51681/user/$a] akka.tcp://ClusterSystem@localhost:51681 is JOINING...
  2. [INFO] [// ::53.930] [ClusterSystem-akka.actor.default-dispatcher-] [akka.tcp://ClusterSystem@localhost:51679/user/$a] akka.tcp://ClusterSystem@localhost:51681 is JOINING...
  3. [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]
  4. [INFO] [// ::54.929] [ClusterSystem-akka.actor.default-dispatcher-] [akka.tcp://ClusterSystem@localhost:2551/user/$a] akka.tcp://ClusterSystem@localhost:51681 is UP!
  1. [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.
  2. [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]
  3. [INFO] [// ::00.808] [ClusterSystem-akka.actor.default-dispatcher-] [akka.tcp://ClusterSystem@localhost:51681/user/$a] akka.tcp://ClusterSystem@localhost:51681 is LEAVING...
  4. [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.
  5. [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]
  6. [INFO] [// ::00.810] [ClusterSystem-akka.actor.default-dispatcher-] [akka.tcp://ClusterSystem@localhost:2551/user/$a] akka.tcp://ClusterSystem@localhost:51681 is LEAVING...
  7. [INFO] [// ::00.933] [ClusterSystem-akka.actor.default-dispatcher-] [akka.tcp://ClusterSystem@localhost:51679/user/$a] akka.tcp://ClusterSystem@localhost:51681 is LEAVING...
  8. [INFO] [// ::01.101] [ClusterSystem-akka.actor.default-dispatcher-] [akka.cluster.Cluster(akka://ClusterSystem)] Cluster Node [akka.tcp://ClusterSystem@localhost:51679] - Shutting down myself
  9. [INFO] [// ::01.102] [ClusterSystem-akka.actor.default-dispatcher-] [akka.cluster.Cluster(akka://ClusterSystem)] Cluster Node [akka.tcp://ClusterSystem@localhost:51679] - Shutting down...
  10. [INFO] [// ::01.104] [ClusterSystem-akka.actor.default-dispatcher-] [akka.cluster.Cluster(akka://ClusterSystem)] Cluster Node [akka.tcp://ClusterSystem@localhost:51679] - Successfully shut down
  11. [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
  12. [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
  13. [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
  1. [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]
  2. [INFO] [// ::02.926] [ClusterSystem-akka.actor.default-dispatcher-] [akka.tcp://ClusterSystem@localhost:2551/user/$a] akka.tcp://ClusterSystem@localhost:51681 is EXITING...
  3. [INFO] [// ::02.927] [ClusterSystem-akka.actor.default-dispatcher-] [akka.cluster.Cluster(akka://ClusterSystem)] Cluster Node [akka.tcp://ClusterSystem@localhost:51681] - Exiting, starting coordinated shutdown
  4. [INFO] [// ::02.927] [ClusterSystem-akka.actor.default-dispatcher-] [akka.tcp://ClusterSystem@localhost:51681/user/$a] akka.tcp://ClusterSystem@localhost:51681 is EXITING...
  5. [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)- 分布式应用开发的一些想法的更多相关文章

  1. Scala 的 Web 框架 Lift 开始 3.0 版本开发

    Scala 的 Web 框架 Lift 开始 3.0 版本开发 http://demo.liftweb.net/ http://liftweb.net/download Lift 框架在不断的成长和改 ...

  2. Akka Cluster简介与基本环境搭建

      akka集群是高容错.去中心化.不存在单点故障以及不存在单点瓶颈的集群.它使用gossip协议通信以及具备故障自动检测功能. Gossip收敛   集群中每一个节点被其他节点监督(默认的最大数量为 ...

  3. 《深入实践Spring Boot》阅读笔记之二:分布式应用开发

    上篇文章总结了<深入实践Spring Boot>的第一部分,这篇文章介绍第二部分:分布式应用开发,以及怎么构建一个高性能的服务平台. 主要从以下几个方面总结: Spring Boot SS ...

  4. akka cluster 初体验

    cluster 配置 akka { actor { provider = "akka.cluster.ClusterActorRefProvider" } remote { log ...

  5. asp.net 分布式应用开发

    Net Framework推出的许多新技术为上述任务的实现提供了相对简单的解决方案.其中,基于SOAP的Web Service在处理分布式应用时具有比传统的DCOM/CORBA明显的优点,结合基于We ...

  6. 高性能分布式应用开发中间件ICE介绍

    作为一个技术人员,你是否在为不断增长的数据量和日益复杂的业务逻辑而头疼不已,杂乱堆砌在一起的庞大业务让系统越来越脆弱,于是你想到了网格,想到了利用分布式来重组一个健壮的系统架构. 随后,RMI,EJB ...

  7. Akka系列(十):Akka集群之Akka Cluster

    前言........... 上一篇文章我们讲了Akka Remote,理解了Akka中的远程通信,其实Akka Cluster可以看成Akka Remote的扩展,由原来的两点变成由多点组成的通信网络 ...

  8. akka cluster sharding source code 学习 (1/5) 替身模式

    为了使一个项目支持集群,自己学习使用了 akka cluster 并在项目中实施了,从此,生活就变得有些痛苦.再配上 apache 做反向代理和负载均衡,debug 起来不要太酸爽.直到现在,我还对 ...

  9. 关于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处理器进行项目的 ...

随机推荐

  1. docker 入门第一步

    docker 安装 利用yum 安装 yum 源更新到最新版本,命令: yum update 需要安装工具 net-tools 命令:yum  install -y net-tools 配置docke ...

  2. dto vo

    不过符合规矩的做法是DTO里可以放各种List<VO>,而VO和entity就是一一对应的关系,vo里不能放entity,entity里也不能放vo,vo和entity只存放和数据库完全相 ...

  3. Match-----Correlation-----find_ncc_model_exposure

    * This example program shows how to use HALCON's correlation-based* matching. In particular it demon ...

  4. 原生js实现Base64编码解码

    注:ie10+ var str = window.btoa("liusong"); console.log(str); var s = window.atob("bGl1 ...

  5. 洛谷P1169 棋盘制作(悬线法)

    题目链接:https://www.luogu.org/problemnew/show/P1169 #include<bits/stdc++.h> #define fi first #def ...

  6. EOS.IO Technical White Paper v2

    [EOS.IO Technical White Paper v2] Abstract: The EOS.IO software introduces a new blockchain architec ...

  7. Python开发【第六篇】:面向对象

    configparser模块 configparser用于处理特定格式的文件,其本质是利用open来操作文件. 文件a.txt [section1] k1 = 123 k2:v2   [section ...

  8. vue绑定html的class属性的方法

    一.对象语法绑定class属性 class的属性代码如下 <style type="text/css"> .red{ color: red; width: 100px; ...

  9. Linux驱动之中断处理体系结构简析

    S3C2440中的中断处理最终是通过IRQ实现的,在Linux驱动之异常处理体系结构简析已经介绍了IRQ异常的处理过程,最终分析到了一个C函数asm_do_IRQ,接下来继续分析asm_do_IRQ, ...

  10. C++学习札记(3)

    一边听着许巍的音乐,一遍学习着C++的精髓,这感觉这酸爽,我一个人体会和知道. 许巍是两代人共同的时代标志,他的音乐作品脍炙人口,堪称经典,经久不衰:此时此刻品味,依然有丰富的各种味道和感情.可能因为 ...