上一篇讨论里我们介绍了几种任务分配(Routing)模式。Akka提供的几种现成智能化Routing模式大多数是通过对用户屏蔽具体的运算Routee选择方式来简化Router使用,提高智能程度,所以我们提到Router的运算是一种无序的运算,消息之间绝对不容许任何形式的依赖,因为向Router发送的消息可能在任何Routee上运算。但是,如果我们能够把运算任务按照任务的类型分配给专门负责处理此等类型任务的Routee,那么我们就可以充分利用Routing模式所带来的运算拓展能力来提高整体运算效率。Akka的ConsistentHashingRouter就是为了满足这样的需求而提供的。ConsistentHashingRouter是通过消息的特征来分辨消息类型,然后自动构建和管理处理各种类型消息的Routees。当然,这就要求系统的消息必须具备预先设定的特征,使ConsistentHashingRouter可以正确分辨并分配给指定的Routee去运算。如果我们确定只有一个Routee负责处理一种类型消息的话,甚至可以在这个Routee中维护某种状态。我们可以设计一个场景来示范ConsistentHashingRouter的应用:模拟一个多货币的存钱盒,分n次随意从盒里取出钱币然后统计各种货币的总额。这个场景中的特征很明显:就是货币种类了,我们把抽出的货币按币种、金额合成消息发给ConsistentHashingRouter。例子里的Routee应该是按照币种由Router自动构建的,维护各种货币当前总额作为内部状态。向ConsistentHashingRouter发送的消息被分配给相应币种的Routee去登记更新货币当前总额。这个统计金额的Routee可以如下定义:

import akka.actor._

val currencies = List("RMB","USD","EUR","JPY","GBP","DEM","HKD","FRF","CHF")

object MoneyCounter {
sealed trait Counting
case class OneHand(cur: String, amt: Double) extends Counting
case class ReportTotal(cur: String) extends Counting
}
class MoneyCounter extends Actor with ActorLogging {
import MoneyCounter._
var currency: String = "RMB"
var amount: Double = override def receive: Receive = {
case OneHand(cur,amt) =>
currency = cur
amount += amt
log.info(s"${self.path.name} received one hand of $amt$cur")
case ReportTotal(_) =>
log.info(s"${self.path.name} has a total of $amount$currency")
}
}

MoneyCounter支持两项功能:一是统计某种货币收到的总额,二是按指令汇报当前总额。我们在前一篇讨论里了解到如果MoneyCounter是Routee类型,那它们应该被视为具相同功能的Actor。而且用户无法分辨或者直接面对某个特定的Routee。任何MoneyCounter都可以收到一手任何货币,不同的货币金额相加结果是错误的。所以我们要用Akka提供的ConsistentHashingRouter来解决这个问题。ConsistentHashingRouter的主要特点是能够分辨消息类型,然后按照消息类型对应到选定的Routee。在我们上面的例子里每个Routee负责一种货币,这样就可以保证每个Routee里的金额总数都是正确的了。ConsistentHashingRouter有三种分辨消息的方法:

1、定义ConsistentHashingRouter的hashMapping函数:这是个PartialFunction[Any,Any],如下:

object HashingRouter extends App {
import MoneyCounter._ val currencies = List("RMB","USD","EUR","JPY","GBP","DEM","HKD","FRF","CHF") val routerSystem = ActorSystem("routerSystem") def mcHashMapping: PartialFunction[Any,Any] = {
case OneHand(cur,_) => cur
case ReportTotal(cur) => cur
} val router = routerSystem.actorOf(ConsistentHashingPool(
nrOfInstances = ,hashMapping = mcHashMapping,virtualNodesFactor = )
.props(MoneyCounter.props),name = "moneyCounter" ) router ! OneHand("RMB",10.00)
router ! OneHand("USD",10.00)
router ! OneHand("HKD",10.00)
router ! OneHand("RMB",10.00)
router ! OneHand("CHF",10.00) router ! ReportTotal("RMB")
router ! ReportTotal("USD") scala.io.StdIn.readLine() routerSystem.terminate()
}

我们在定义router时直接把mcHashingMapping传到ConsistentHashingPool的构建器里就行了。特别要注意nrOfInstances,这个参数必须比消息类型的数量大才行,否则Router会错误引导消息。测试运算结果显示如下:

INFO] [// ::09.334] [routerSystem-akka.actor.default-dispatcher-] [akka://routerSystem/user/moneyCounter/$e] $e received one hand of 10.0RMB
[INFO] [// ::09.334] [routerSystem-akka.actor.default-dispatcher-] [akka://routerSystem/user/moneyCounter/$b] $b received one hand of 10.0USD
[INFO] [// ::09.334] [routerSystem-akka.actor.default-dispatcher-] [akka://routerSystem/user/moneyCounter/$d] $d received one hand of 10.0CHF
[INFO] [// ::09.334] [routerSystem-akka.actor.default-dispatcher-] [akka://routerSystem/user/moneyCounter/$a] $a received one hand of 10.0HKD
[INFO] [// ::09.334] [routerSystem-akka.actor.default-dispatcher-] [akka://routerSystem/user/moneyCounter/$e] $e received one hand of 10.0RMB
[INFO] [// ::09.337] [routerSystem-akka.actor.default-dispatcher-] [akka://routerSystem/user/moneyCounter/$b] $b has a total of 10.0USD
[INFO] [// ::09.337] [routerSystem-akka.actor.default-dispatcher-] [akka://routerSystem/user/moneyCounter/$e] $e has a total of 20.0RMB

Router自动调用了e,b,d,a4个Routees,并且能把消息引导到正确的Routee。

2、可以让消息继承ConsistentHashable,如此我们要在消息里实现函数constentHashKey, 如下:

object MoneyCounter {
sealed class Counting(cur: String) extends ConsistentHashable {
override def consistentHashKey: Any = cur
}
case class OneHand(cur: String, amt: Double) extends Counting(cur)
case class ReportTotal(cur: String) extends Counting(cur)
def props = Props(new MoneyCounter)
}

现在消息都是ConsistentHashable类型的了。构建新的Router来测试效果:

  val router = routerSystem.actorOf(ConsistentHashingPool(
nrOfInstances = , virtualNodesFactor = ).props(
MoneyCounter.props),name = "moneyCounter") router ! OneHand("RMB",10.00)
router ! OneHand("USD",10.00)
router ! OneHand("HKD",10.00)
router ! OneHand("RMB",10.00)
router ! OneHand("CHF",10.00) router ! ReportTotal("RMB")
router ! ReportTotal("USD")

运算结果同样正确:

[INFO] [// ::29.746] [routerSystem-akka.actor.default-dispatcher-] [akka://routerSystem/user/moneyCounter/$e] $e received one hand of 10.0RMB
[INFO] [// ::29.746] [routerSystem-akka.actor.default-dispatcher-] [akka://routerSystem/user/moneyCounter/$b] $b received one hand of 10.0USD
[INFO] [// ::29.746] [routerSystem-akka.actor.default-dispatcher-] [akka://routerSystem/user/moneyCounter/$a] $a received one hand of 10.0HKD
[INFO] [// ::29.746] [routerSystem-akka.actor.default-dispatcher-] [akka://routerSystem/user/moneyCounter/$d] $d received one hand of 10.0CHF
[INFO] [// ::29.746] [routerSystem-akka.actor.default-dispatcher-] [akka://routerSystem/user/moneyCounter/$e] $e received one hand of 10.0RMB
[INFO] [// ::29.749] [routerSystem-akka.actor.default-dispatcher-] [akka://routerSystem/user/moneyCounter/$e] $e has a total of 20.0RMB
[INFO] [// ::29.749] [routerSystem-akka.actor.default-dispatcher-] [akka://routerSystem/user/moneyCounter/$b] $b has a total of 10.0USD

3、直接把消息包在ConsistentHashableEnvelope里:

  router ! ConsistentHashableEnvelope(message = OneHand("RMB",23.00),hashKey = "RMB")

这种方式需要用户手工指定Routee,如果用这种方式,我们其实不必用Router,直接把消息传给专职的Actor就行了。

看来还是第二种方法比较合适。因为比起第一种方法多了类型安全和与Router的松散耦合。下面就是一个用第二种方法的完整示范源代码:

import akka.actor._
import akka.routing.ConsistentHashingRouter.{ConsistentHashMapping, ConsistentHashable, ConsistentHashableEnvelope}
import akka.routing._ object MoneyCounter {
sealed class Counting(cur: String) extends ConsistentHashable {
override def consistentHashKey: Any = cur
}
case class OneHand(cur: String, amt: Double) extends Counting(cur)
case class ReportTotal(cur: String) extends Counting(cur)
def props = Props(new MoneyCounter)
}
class MoneyCounter extends Actor with ActorLogging {
import MoneyCounter._
var currency: String = "RMB"
var amount: Double = override def receive: Receive = {
case OneHand(cur,amt) =>
currency = cur
amount += amt
log.info(s"${self.path.name} received one hand of $amt$cur")
case ReportTotal(_) =>
log.info(s"${self.path.name} has a total of $amount$currency")
}
}
object HashingRouter extends App {
import MoneyCounter._
import scala.util.Random val currencies = List("RMB","USD","EUR","JPY","GBP","DEM","HKD","FRF","CHF") val routerSystem = ActorSystem("routerSystem") val router = routerSystem.actorOf(ConsistentHashingPool(
nrOfInstances = currencies.size+, virtualNodesFactor = ).props(
MoneyCounter.props),name = "moneyCounter") ( to ).toList foreach (_ => router ! OneHand(
currencies(Random.nextInt(currencies.size-))
,Random.nextInt() * 1.00)) currencies foreach (c => router ! ReportTotal(c)) scala.io.StdIn.readLine() routerSystem.terminate()
}

Akka(5): ConsistentHashing Router - 可选定Routee的任务分配模式的更多相关文章

  1. (转)akka Router实例

    通常在分布式任务调度系统中会有这样的需求:一组actor提供相同的服务,我们在调用任务的时候只需要选择其中一个actor进行处理即可. 其实这就是一个负载均衡或者说路由策略,akka作为一个高性能支持 ...

  2. AKKA Router路由

    路由概念 大量的actor在并行工作的时候,处理到来的消息流,这时候就需要一个组件或者东西来引导消息从源到目的地Actor,这个组件或者东西就是Router在Akka中,router也是一种actor ...

  3. AKKA集群中的分布式发布订阅

    集群中的分布式发布订阅 如何向一个不知道在哪个节点上运行的actor发送消息呢? 如何向集群中的所有actor发送感兴趣的主题的消息? 这种模式提供了一个中介actor,akka.cluster.pu ...

  4. akka框架——异步非阻塞高并发处理框架

    akka actor, akka cluster akka是一系列框架,包括akka-actor, akka-remote, akka-cluster, akka-stream等,分别具有高并发处理模 ...

  5. Akka源码分析-Router

    akak中还有一个比较重要的概念,那就是Router(路由).路由的概念,相信大家都不陌生,在akka中,它就是其他actors的一个代理,会把消息按照路由规则,分发给指定的actor.我一般喜欢把R ...

  6. Akka(4): Routers - 智能任务分配

    Actor模式最大的优点就是每个Actor都是一个独立的任务运算器.这种模式让我们很方便地把一项大型的任务分割成若干细小任务然后分配给不同的Actor去完成.优点是在设计时可以专注实现每个Actor的 ...

  7. Akka(11): 分布式运算:集群-均衡负载

    在上篇讨论里我们主要介绍了Akka-Cluster的基本原理.同时我们也确认了几个使用Akka-Cluster的重点:首先,Akka-Cluster集群构建与Actor编程没有直接的关联.集群构建是A ...

  8. Akka(13): 分布式运算:Cluster-Sharding-运算的集群分片

    通过上篇关于Cluster-Singleton的介绍,我们了解了Akka为分布式程序提供的编程支持:基于消息驱动的运算模式特别适合分布式程序编程,我们不需要特别的努力,只需要按照普通的Actor编程方 ...

  9. Akka(22): Stream:实时操控:动态管道连接-MergeHub,BroadcastHub and PartitionHub

    在现实中我们会经常遇到这样的场景:有一个固定的数据源Source,我们希望按照程序运行状态来接驳任意数量的下游接收方subscriber.又或者我需要在程序运行时(runtime)把多个数据流向某个固 ...

随机推荐

  1. JS window对象的top、parent、opener含义介绍 以及防止网页被嵌入框架的代码

    1.top该变更永远指分割窗口最高层次的浏览器窗口.如果计划从分割窗口的最高层次开始执行命令,就可以用top变量. 2.openeropener用于在window.open的页面引用执行该window ...

  2. 初识JS

    今儿我遇到一特别恐怖的事儿,JS 刚开始的我看到JS感觉是懵逼的,翻开第一页,感觉是棒棒哒,再看第二页,感觉是easy的,看到第三页是恐怖的,当看到的第四页的时候,我感觉今年的清明节是为我准备的 废话 ...

  3. CSS清除float浮动

    一.浮动产生原因   -   TOP 一般浮动是什么情况呢?一般是一个盒子里使用了CSS float浮动属性,导致父级对象盒子不能被撑开,这样CSS float浮动就产生了. 本来两个黑色对象盒子是在 ...

  4. dispatch_group_t 日常使用注意事项

    一.背景简介平时在进行多线程处理任务时,有时候希望多个任务之间存在着一种联系,希望在所有的任务执行完后做一些总结性处理.那么就可以将多个任务放在一个任务组中进行统一管理.dispatch提供了相应的A ...

  5. 使用validator-api来验证spring-boot的参数

    作为服务端开发,验证前端传入的参数的合法性是一个必不可少的步骤,但是验证参数是一个基本上是一个体力活,而且冗余代码繁多,也影响代码的可阅读性,所以有没有一个比较优雅的方式来解决这个问题? 这么简单的问 ...

  6. webrtc学习笔记1(建立连接基本流程)

    最近在做一个基于webrtc的视频软件,以下是自己对于上层建立通话连接流程的基本理解,记录于此. 假设A和B要建立视频通话,A为房间创建端,B为加入房间端: 1.A通过http登录.获取其他服务器地址 ...

  7. jdk源码剖析五:JDK8-废弃永久代(PermGen)迎来元空间(Metaspace)

    目录 1.背景 2.为什么废弃永久代(PermGen) 3.深入理解元空间(Metaspace) 4.总结 ========正文分割线===== 一.背景 1.1 永久代(PermGen)在哪里? 根 ...

  8. bootstrap快速入门笔记(三)响应式,行,列,偏移量,排序

    一,响应式列重置 .clearfix <div class="row"> <div class="col-xs-6 col-sm-3"> ...

  9. angularjs中常用的ng指令介绍【转载】

    原文:http://www.cnblogs.com/lvdabao/p/3379659.html 一.模板中可使用的东西及表达式 模板中可以使用的东西包括以下四种: 指令(directive).ng提 ...

  10. [移动端] IOS下border-image不起作用的解决办法

    上周五突然接到现场的一个需求,做一个移动端的劳模展示页面.现场美工把原型图发了过来.这个样子的: 说实在的很想吐槽一下我们美工的审美哈,不过这不是重点. 因为边框是需要特殊花纹的所以打算用border ...