集群感知路由器和集群分片的不同用例?
Different use case for akka cluster aware router & akka cluster sharding?
集群感知路由器:
val router = system.actorOf(ClusterRouterPool(
RoundRobinPool(0),
ClusterRouterPoolSettings(
totalInstances = 20,
maxInstancesPerNode = 1,
allowLocalRoutees = false,
useRole = None
)
).props(Props[Worker]), name = "router")
在这里,我们可以发送消息给router
,消息会发送给一系列远程routee actor。
集群分片(不考虑持久化)
class NewShoppers extends Actor {
ClusterSharding(context.system).start(
"shardshoppers",
Props(new Shopper),
ClusterShardingSettings(context.system),
Shopper.extractEntityId,
Shopper.extractShardId
)
def proxy = {
ClusterSharding(context.system).shardRegion("shardshoppers")
}
override def receive: Receive = {
case msg => proxy forward msg
}
}
在这里,我们可以发送消息到proxy
,消息将发送到一系列分片actor(a.k.a.entities)。
所以,我的问题是:it seems both 2 methods can make the tasks distribute to a lot of actors. What's the design choice of above two? Which situation need which choice?
当您只想将一些工作发送到任何节点并进行一些处理时,池路由器将是这样,按顺序发送的两条消息可能不会在同一个 actor 中结束以进行处理。
集群分片适用于当您在某种类型的每个 actor 上都有一个唯一的 id,并且它们太多而无法放入一个节点,但您希望具有该 id 的每条消息总是在 actor 中结束对于那个ID。例如,将 User
建模为一个实体,您希望所有关于该用户的命令都以该用户结束,但您希望在集群拓扑发生变化(删除或添加节点)时移动参与者,并且您希望它们合理在现有节点之间保持平衡。
感谢 johanandren 和链接文章作为以下答案的基础:
router
和 sharding
都分配工作。 Sharding
是必需的,如果除了负载平衡之外,接收者参与者还必须可靠地管理与 entity identifier
直接关联的状态。
回顾一下,entity identifier
是一个密钥,从正在发送的消息中派生,确定消息在集群中的接收者角色。
首先,您能否使用一致的散列路由器管理与跨不同节点的 identifier
关联的状态? Consistent Hash
路由器将始终向同一目标参与者发送具有相等 identifier
的消息。答案是:没有,如下所述。
当集群中的节点关闭或启动时,hash-based 方法停止工作,因为这会更改某些标识符的关联参与者。如果一个节点出现故障,与其关联的消息现在将发送到网络中的另一个参与者,但该参与者不会被告知它现在正在替换的参与者的先前状态。同样,如果出现新节点,它将处理先前与不同参与者相关联的消息(标识符),并且新节点或旧节点都不会被告知这一点。
另一方面,通过分片,创建的参与者知道他们管理的entity identifier
。 Sharding
将确保只有一个参与者管理集群中的实体。如果他们的父节点出现故障,它将 re-create 在不同的节点上分片演员。因此,当节点数量发生变化时,使用 persistence
它们将跨节点保留其(持久)状态。如果一个 actor re-created 在不同的节点上,你也不必担心并发问题,这要归功于分片。此外,如果遇到带有新 entity identifier
的消息,而该消息的参与者尚不存在,则会创建一个新的参与者。
一致的散列路由器可能仍可用于缓存,因为具有相同密钥的消息通常确实会发送给同一个参与者。要管理在集群中仅存在一次的有状态实体,需要 Sharding
。
使用路由器进行负载平衡,使用Sharding
以分布式方式管理有状态实体。
集群感知路由器:
val router = system.actorOf(ClusterRouterPool( RoundRobinPool(0), ClusterRouterPoolSettings( totalInstances = 20, maxInstancesPerNode = 1, allowLocalRoutees = false, useRole = None ) ).props(Props[Worker]), name = "router")
在这里,我们可以发送消息给
router
,消息会发送给一系列远程routee actor。集群分片(不考虑持久化)
class NewShoppers extends Actor { ClusterSharding(context.system).start( "shardshoppers", Props(new Shopper), ClusterShardingSettings(context.system), Shopper.extractEntityId, Shopper.extractShardId ) def proxy = { ClusterSharding(context.system).shardRegion("shardshoppers") } override def receive: Receive = { case msg => proxy forward msg } }
在这里,我们可以发送消息到
proxy
,消息将发送到一系列分片actor(a.k.a.entities)。
所以,我的问题是:it seems both 2 methods can make the tasks distribute to a lot of actors. What's the design choice of above two? Which situation need which choice?
当您只想将一些工作发送到任何节点并进行一些处理时,池路由器将是这样,按顺序发送的两条消息可能不会在同一个 actor 中结束以进行处理。
集群分片适用于当您在某种类型的每个 actor 上都有一个唯一的 id,并且它们太多而无法放入一个节点,但您希望具有该 id 的每条消息总是在 actor 中结束对于那个ID。例如,将 User
建模为一个实体,您希望所有关于该用户的命令都以该用户结束,但您希望在集群拓扑发生变化(删除或添加节点)时移动参与者,并且您希望它们合理在现有节点之间保持平衡。
感谢 johanandren 和链接文章作为以下答案的基础:
router
和 sharding
都分配工作。 Sharding
是必需的,如果除了负载平衡之外,接收者参与者还必须可靠地管理与 entity identifier
直接关联的状态。
回顾一下,entity identifier
是一个密钥,从正在发送的消息中派生,确定消息在集群中的接收者角色。
首先,您能否使用一致的散列路由器管理与跨不同节点的 identifier
关联的状态? Consistent Hash
路由器将始终向同一目标参与者发送具有相等 identifier
的消息。答案是:没有,如下所述。
当集群中的节点关闭或启动时,hash-based 方法停止工作,因为这会更改某些标识符的关联参与者。如果一个节点出现故障,与其关联的消息现在将发送到网络中的另一个参与者,但该参与者不会被告知它现在正在替换的参与者的先前状态。同样,如果出现新节点,它将处理先前与不同参与者相关联的消息(标识符),并且新节点或旧节点都不会被告知这一点。
另一方面,通过分片,创建的参与者知道他们管理的entity identifier
。 Sharding
将确保只有一个参与者管理集群中的实体。如果他们的父节点出现故障,它将 re-create 在不同的节点上分片演员。因此,当节点数量发生变化时,使用 persistence
它们将跨节点保留其(持久)状态。如果一个 actor re-created 在不同的节点上,你也不必担心并发问题,这要归功于分片。此外,如果遇到带有新 entity identifier
的消息,而该消息的参与者尚不存在,则会创建一个新的参与者。
一致的散列路由器可能仍可用于缓存,因为具有相同密钥的消息通常确实会发送给同一个参与者。要管理在集群中仅存在一次的有状态实体,需要 Sharding
。
使用路由器进行负载平衡,使用Sharding
以分布式方式管理有状态实体。