我目前正在使用基于以下内容的实现:
org.springframework.integration.support.leader.LockRegistryLeaderInitiator
具有多个候选角色,以便群集中只有一个应用程序实例被选为每个角色的领导者。在初始化集群期间,如果autoStartup属性设置为true,则初始化的第一个应用程序实例将被选为所有角色的领导者。这是我们想要避免的事情,而是在整个集群中公平分配主角。
上面的一个可能的解决方案可能是当集群准备就绪并正确初始化时,然后调用将执行的端点:
lockRegistryLeaderInitiator.start()
对于群集中的所有实例,以便选举过程开始,角色在实例之间公平分配。其中一个缺点是,这需要成为部署过程的一部分,增加了某种程度的复杂性。
上述建议的最佳做法是什么?是否有任何相关附加功能的计划?例如,仅当X应用程序实例可用时才自动启动领导者选举?
答案 0 :(得分:1)
我建议你看看Spring Cloud Bus项目。我不知道它的详细信息,但看起来您对所有autoStartup = false
实例的LockRegistryLeaderInitiator
的想法以及通过某些分布式事件启动它们是可行的方法。
不确定我们可以从Spring Integration的角度为您做些什么,但它完全感觉不是它的责任,所有协调和重新平衡都应该通过其他工具完成。幸运的是,我们所有的Spring项目都可以作为一个平台一起使用。
我认为通过总线,您甚至可以跟踪加入群集的实例数量,并决定您自己何时以及如何发布StartLeaderInitiators
事件。
答案 1 :(得分:1)
使用Zookeeper LeaderInitiator
会相对容易,因为您可以在启动它之前检查zookeeper中的实例计数。
使用锁定注册表并不容易,因为没有关于实例的固有信息;你需要一些外部机制(比如zookeeper,在这种情况下,你也可以使用ZK)。
或者,您可以使用Spring Cloud Bus(使用RabbitMQ或Kafka)向所有实例发送信号,以便开始选举领导。
答案 2 :(得分:0)
我发现这样做的方法非常简单。 您可以向每个节点添加计划任务,如果节点拥有太多领导权,这些任务会定期尝试产生领导权。
例如,如果您有 N 个节点和 2*N 个角色,并且您希望实现完全公平的领导权分配(每个节点试图仅拥有两个领导权),您可以使用以下方法:
@Component
@RequiredArgsConstructor
public class FairLeaderDistributor {
private final List<LeaderInitiator> initiators;
@Scheduled(fixedDelay = 300_000) // once per 5 minutes
public void yieldExcessLeaderships() {
initiators.stream()
.map(LeaderInitiator::getContext)
.filter(Context::isLeader)
.skip(2) // keep only 2 leaderships
.forEach(Context::yield);
}
}
当所有节点都启动时,您将最终获得完全公平的领导权分配。
如果您使用 Zookeeper LeaderInitiator
实现,您还可以根据当前活动节点数实现动态分配。
可以从 Curator LeaderSelector::getParticipants
方法轻松检索当前参与者数量。
您可以通过 LeaderSelector
字段的反射获得 LeaderInitiator.leaderSelector
。
@Slf4j
@Component
@RequiredArgsConstructor
public class DynamicFairLeaderDistributor {
final List<LeaderInitiator> initiators;
@SneakyThrows
private static int getParticipantsCount(LeaderInitiator leaderInitiator) {
Field field = LeaderInitiator.class.getDeclaredField("leaderSelector");
field.setAccessible(true);
LeaderSelector leaderSelector = (LeaderSelector) field.get(leaderInitiator);
return leaderSelector.getParticipants().size();
}
@Scheduled(fixedDelay = 5_000)
public void yieldExcessLeaderships() {
int rolesCount = initiators.size();
if (rolesCount == 0) return;
int participantsCount = getParticipantsCount(initiators.get(0));
if (participantsCount == 0) return;
int maxLeadershipsCount = (rolesCount - 1) / participantsCount + 1;
log.info("rolesCount={}, participantsCount={}, maxLeadershipsCount={}", rolesCount, participantsCount, maxLeadershipsCount);
initiators.stream()
.map(LeaderInitiator::getContext)
.filter(Context::isLeader)
.skip(maxLeadershipsCount)
.forEach(Context::yield);
}
}