Akka默认分片策略LeastShardAllocationStrategy源码剖析

LeastShardAllocationStrategy 是 Akka Cluster Sharding 默认使用的分片分配策略,主要实现负载均衡动态再平衡功能。

下面我将重点分析其核心实现,特别是 allocateShardrebalance 方法,以及关键参数的作用。

1. 构造方法参数分析

class LeastShardAllocationStrategy(
  rebalanceThreshold: Int = 10,
  maxSimultaneousRebalance: Int = 3
) extends ShardAllocationStrategy with Logging

关键参数:

  • rebalanceThreshold (默认=10):

    • 触发再平衡的阈值差
    • 当某个节点的分片数比最少分片节点多出这个数量时,触发再平衡
    • 值越大,再平衡频率越低
  • maxSimultaneousRebalance (默认=3):

    • 同时进行的最大再平衡操作数
    • 控制再平衡的并发度,避免网络和资源过载

2. allocateShard 方法实现分析

override def allocateShard(
  requester: ActorRef,
  shardId: ShardId,
  currentShardAllocations: Map[ActorRef, IndexedSeq[ShardId]]
): Future[ActorRef] = {
  
  // 1. 找出当前分片数最少的节点
  val (minNode, minCount) = currentShardAllocations
    .map { case (n, shards) => n -> shards.size }
    .minBy { case (_, count) => count }

  // 2. 如果有多个最少分片节点,随机选择一个
  val candidates = currentShardAllocations.collect {
    case (n, shards) if shards.size == minCount => n
  }.toVector

  // 3. 随机选择(避免总是选择第一个)
  val selected = candidates(ThreadLocalRandom.current().nextInt(candidates.size))
  
  Future.successful(selected)
}

关键点:

  1. 纯负载均衡策略

    • 只考虑各节点当前持有的分片数量
    • 不考虑分片大小、资源使用率等其他因素
  2. 随机选择

    • 当多个节点具有相同最少分片数时,随机选择
    • 避免总是选择同一个节点
  3. 同步返回

    • 直接返回 Future.successful,没有异步操作
    • 分配决策是轻量级的

3. rebalance 方法实现分析

override def rebalance(
  currentShardAllocations: Map[ActorRef, IndexedSeq[ShardId]],
  rebalanceInProgress: Set[ShardId]
): Future[Set[ShardId]] = {
  
  // 1. 计算各节点分片数
  val allocations = currentShardAllocations
    .map { case (n, shards) => n -> shards.size }
    .toMap

  // 2. 找出最少和最多分片的节点
  val (minNode, minCount) = allocations.minBy { case (_, count) => count }
  val (maxNode, maxCount) = allocations.maxBy { case (_, count) => count }

  // 3. 检查是否需要再平衡
  if (maxCount - minCount > rebalanceThreshold) {
    // 4. 计算可迁移的分片数(考虑并发限制)
    val numberOfShards = math.min(
      maxCount - minCount - rebalanceThreshold,
      maxSimultaneousRebalance - rebalanceInProgress.size
    )

    if (numberOfShards > 0) {
      // 5. 选择要迁移的分片(排除正在迁移的)
      val candidateShards = currentShardAllocations(maxNode)
        .filterNot(rebalanceInProgress)
        .take(numberOfShards)
        .toSet

      Future.successful(candidateShards)
    } else {
      Future.successful(Set.empty)
    }
  } else {
    Future.successful(Set.empty)
  }
}

关键点:

  1. 阈值触发机制

    • 只有当 maxCount - minCount > rebalanceThreshold 时才触发
    • 避免频繁的再平衡操作
  2. 并发控制

    • maxSimultaneousRebalance 限制同时迁移的分片数
    • 计算方式:maxSimultaneousRebalance - rebalanceInProgress.size
  3. 安全选择分片

    • 过滤掉正在迁移的分片 (filterNot(rebalanceInProgress))
    • 从最多分片的节点选择分片
  4. 保守迁移策略

    • 每次只迁移足够消除阈值差的分片数
    • 不会一次性平衡到完全平均

4. 策略特点总结

特性说明
分配策略始终选择当前分片最少的节点
再平衡触发基于固定阈值差 (rebalanceThreshold)
并发控制限制同时迁移数 (maxSimultaneousRebalance)
优点实现简单,资源开销小
局限不考虑分片负载差异,仅基于计数

5. 调优建议

  1. 增大 rebalanceThreshold

    • 当集群规模大且分片迁移成本高时
    • 减少不必要的再平衡操作
  2. 调整 maxSimultaneousRebalance

    • 在高速网络环境中可以增加
    • 在资源受限环境中减少
  3. 监控指标

    // 监控分片分配差异
    val maxDiff = allocations.values.max - allocations.values.min
    // 监控再平衡队列
    val rebalanceQueueSize = rebalanceInProgress.size
    

这种策略适合大多数均匀负载的场景,但如果您的分片有显著不同的资源需求(如您之前提到的大内存Actor),则需要像您之前实现的 WeightedShardAllocationStrategy 这样的自定义策略。

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值