一、介绍 

当您需要在集群中的多个节点之间分配Actor,并希望能够使用其逻辑标识符与它们进行交互时,集群分片是非常有用的。你无需关心Actor在集群中的物理位置,因为这可能也会随着时间的推移而发生变化。

例如,它可以是代表域驱动设计术语中聚合根的参与者。在这里,我们称这些Actore为“实体”。这些Actor通常具有持久状态,但此功能不仅限于具有持久化状态的Actor。

当你有一个很消耗资源的Actor,例如占内存或者CPU,把它放在一台机器上可能吃不消,这时候集群分片就能够提供很好的帮助,将这些Actor分散在集群中的多个结点上。

二、依赖

<dependency>
<groupId>com.typesafe.akka</groupId>
<artifactId>akka-cluster-sharding_2.12</artifactId>
<version>2.5.18</version>
</dependency>

三、例子

假设我们有如下一个需要使用集群分片模式的实体Actor:

case object Increment
case object Decrement
final case class Get(counterId: Long)
final case class EntityEnvelope(id: Long, payload: Any) case object Stop
final case class CounterChanged(delta: Int) class Counter extends PersistentActor {
import ShardRegion.Passivate context.setReceiveTimeout(120.seconds) // self.path.name is the entity identifier (utf-8 URL-encoded)
override def persistenceId: String = "Counter-" + self.path.name var count = 0 def updateState(event: CounterChanged): Unit =
count += event.delta override def receiveRecover: Receive = {
case evt: CounterChanged ⇒ updateState(evt)
} override def receiveCommand: Receive = {
case Increment ⇒ persist(CounterChanged(+1))(updateState)
case Decrement ⇒ persist(CounterChanged(-1))(updateState)
case Get(_) ⇒ sender() ! count
case ReceiveTimeout ⇒ context.parent ! Passivate(stopMessage = Stop)
case Stop ⇒ context.stop(self)
}
}

上面这个Actor使用PersistActor中的事件来源模式存储其内部状态,当然其不一定需要是持久化的Actor。使用持久化的好处是,如果节点之间的实体发生故障或迁移,它能够恢复其状态。

我们要使用集群的分片模式,通常就需要在群集中每个节点上的系统启动时,使用ClusterSharding.start方法注册支持的实体类型,这样我们就能在所有将承载分片的节点上运行这个方法来部署分片。其中,ClusterSharding.start如下:

val counterRegion: ActorRef = ClusterSharding(system).start(
typeName = "Counter",
entityProps = Props[Counter],
settings = ClusterShardingSettings(system),
extractEntityId = extractEntityId,
extractShardId = extractShardId)

其中,start方法返回了ShardRegion,是个ActorRef类型。ShardRegion是一个特殊的Actor,负责管理可能多个分片(shard)内称为Entity的Actor实例。这些分片可能是分布在不同的集群节点上的,外界通过ShardRegion与其辖下Entities沟通。从start函数参数entityProps我们看到:每个分片中只容许一个种类的Actor;具体的Entity实例是由另一个内部Actor即shard构建的,shard可以在一个分片中构建多个Entity实例。多shard多entity的特性可以从extractShardId,extractEntityId这两个方法中得到一些信息。我们说过Actor自编码即entity-id是Cluster-Sharding的核心元素。在entity-id这个自编码中还包含了shard-id,所以用户可以通过entity-id的编码规则来设计整个分片系统包括每个ShardRegion下shard和entity的数量。当ShardRegion得到一个entity-id后,首先从中抽取shard-id,如果shard-id在集群中不存在的话就按集群各节点负载情况在其中一个节点上构建新的shard;然后再用entity-id在shard-id分片中查找entity,如果不存在就构建一个新的entity实例。整个shard和entity的构建过程都是通过用户提供的函数extractShardId和extractEntityId实现的,Cluster-Sharding就是通过这两个函数按用户的要求来构建和使用shard和entity的。下面我们看下这种自编码的例子:

val extractEntityId: ShardRegion.ExtractEntityId = {
case EntityEnvelope(id, payload) ⇒ (id.toString, payload)
case msg @ Get(id) ⇒ (id.toString, msg)
} val numberOfShards = 100 val extractShardId: ShardRegion.ExtractShardId = {
case EntityEnvelope(id, _) ⇒ (id % numberOfShards).toString
case Get(id) ⇒ (id % numberOfShards).toString
case ShardRegion.StartEntity(id) ⇒
// StartEntity is used by remembering entities feature
(id.toLong % numberOfShards).toString
}

在大多数情况下工作正常的简单分片算法是获取hashCode实体标识符的模数为分数的绝对值。下面我们可以通过如下程序测试:

val counterRegion: ActorRef = ClusterSharding(system).shardRegion("Counter")
counterRegion ! Get(123)
expectMsg(0) counterRegion ! EntityEnvelope(123, Increment)
counterRegion ! Get(123)
expectMsg(1)

Akka Cluster之集群分片的更多相关文章

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

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

  2. akka 集群分片

    akka 集群 Sharding分片 分片上下级结构 集群(多台节点机) —> 每台节点机(1个片区) —> 每个片区(多个分片) —> 每个分片(多个实体) 实体: 分片管理的 A ...

  3. akka-typed(7) - cluster:sharding, 集群分片

    在使用akka-typed的过程中发现有很多地方都简化了不少,变得更方便了,包括:Supervision,只要用Behaviors.supervise()把Behavior包住,很容易就可以实现这个a ...

  4. Akka-Cluster(6)- Cluster-Sharding:集群分片,分布式交互程序核心方式

    在前面几篇讨论里我们介绍了在集群环境里的一些编程模式.分布式数据结构及具体实现方式.到目前为止,我们已经实现了把程序任务分配给处于很多服务器上的actor,能够最大程度的利用整体系统的硬件资源.这是因 ...

  5. 超详细的 Redis Cluster 官方集群搭建指南

    今天从 0 开始搭建 Redis Cluster 官方集群,解决搭建过程中遇到的问题,超详细. 安装ruby环境 因为官方提供的创建集群的工具是用ruby写的,需要ruby2.2.2+版本支持,rub ...

  6. MySQL Cluster(MySQL 集群) 初试(转)

    作/译者:叶金荣(imysql#imysql.com>),来源:http://imysql.com,欢迎转载. 作/译者:叶金荣(Email: ),来源:http://imysql.cn,转载请 ...

  7. mongodb集群+分片部署(二)

    机器:10.165.38.68    10.165.38.72 部署包:mongodb-linux-x86_64-rhel55-3.0.2.tgz(百度云盘下载地址:http://pan.baidu. ...

  8. Redis Cluster(集群)的搭建

    一.Redis的下载.安装.启动(单实例) 我们统一将Redis安装在/opt目录下,执行命令如下: $ cd /opt $ wget http://download.redis.io/release ...

  9. MySQL Cluster(MySQL 集群) 初试

    MySQL Cluster 是MySQL适合于分布式计算环境的高实用.高冗余版本.它采用了NDB Cluster 存储引擎,允许在1个 Cluster 中运行多个MySQL服务器.在MyQL 5.0及 ...

随机推荐

  1. (原)Unreal渲染模块 源码和实例分析说明

    @author:白袍小道 说明 1.由于小道就三境武夫而已,而UE渲染部分不仅管理挺大,而且牵扯技术和内容驳杂,所以才有这篇梳理. 2.尽量会按书籍和资料,源码,小模块的调试和搬山(就是敲键盘)..等 ...

  2. sharePreference的几个重点

    一.  SharePreferences是用来存储一些简单配置信息的一种机制,使用Map数据结构来存储数据,以键值对的方式存储,采用了XML格式将数据存储到设备中,文件存放在/data/data/&l ...

  3. NYOJ 42 一笔画

    一笔画问题 时间限制:3000 ms  |  内存限制:65535 KB 难度:4   描述 zyc从小就比较喜欢玩一些小游戏,其中就包括画一笔画,他想请你帮他写一个程序,判断一个图是否能够用一笔画下 ...

  4. CodeForces Round #515 Div.3 A. Vova and Train

    http://codeforces.com/contest/1066/problem/A Vova plans to go to the conference by train. Initially, ...

  5. PHP异常处理类(文件上传提示)

    知识点: 大部分时候我们的代码总有各种各样的bug,新手程序员(比如我)最经常的工作就是不停的报错和echo变量,一个好的异常处理类可以帮我们更快+更容易理解报错代码的问题,同时,异常处理还可以避免一 ...

  6. vs 2012 未能找到与约束contractName Microsoft.VisualStudio.Utilities...匹配的导出

    系统自动更新后,打开项目进行维护时,居然出错了,报的错误信息为“未能找到与约束contractName Microsoft.VisualStudio.Utilities...匹配的导出” 上网查了下, ...

  7. 【bzoj3585/bzoj3339】mex/Rmq Problem 莫队算法+分块

    原文地址:http://www.cnblogs.com/GXZlegend/p/6805283.html 题目描述 有一个长度为n的数组{a1,a2,...,an}.m次询问,每次询问一个区间内最小没 ...

  8. P3531 [POI2012]LIT-Letters

    题目描述 Little Johnny has a very long surname. Yet he is not the only such person in his milieu. As it ...

  9. Where to Run LightOJ - 1287(概率dp)

    Where to Run LightOJ - 1287(概率dp) 题面长长的,看了半天也没看懂题意 不清楚的地方,如何判断一个点是否是EJ 按照我的理解 在一个EJ点处,要么原地停留五分钟接着走,要 ...

  10. PHP:在class中定义常量注意事项

    一.不能在成员函数中定义常量,否则会引发诡异地语法错误 syntax error, unexpected 'CONST' (T_CONST) 示例 /* 错误的方式 */ class A { publ ...