io.atomix.cluster.impl.DiscoveryMembershipProtocol Maven / Gradle / Ivy
The newest version!
/*
* Copyright Camunda Services GmbH and/or licensed to Camunda Services GmbH under
* one or more contributor license agreements. See the NOTICE file distributed
* with this work for additional information regarding copyright ownership.
* Licensed under the Camunda License 1.0. You may not use this file
* except in compliance with the Camunda License 1.0.
*/
package io.atomix.cluster.impl;
import io.atomix.cluster.BootstrapService;
import io.atomix.cluster.Member;
import io.atomix.cluster.MemberId;
import io.atomix.cluster.discovery.NodeDiscoveryEvent;
import io.atomix.cluster.discovery.NodeDiscoveryEventListener;
import io.atomix.cluster.discovery.NodeDiscoveryService;
import io.atomix.cluster.protocol.GroupMembershipEvent;
import io.atomix.cluster.protocol.GroupMembershipEventListener;
import io.atomix.cluster.protocol.GroupMembershipProtocol;
import io.atomix.cluster.protocol.GroupMembershipProtocolConfig;
import io.atomix.utils.event.AbstractListenerManager;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicBoolean;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* A {@link GroupMembershipProtocol} implementation which uses the node discovery service to add and
* remove new members. Members can be either initially provided as is, or will be added/removed as
* nodes are added/removed in the associated {@link NodeDiscoveryService} passed during {@link
* #join(BootstrapService, NodeDiscoveryService, Member)}.
*/
public final class DiscoveryMembershipProtocol
extends AbstractListenerManager
implements GroupMembershipProtocol, NodeDiscoveryEventListener {
private static final Logger LOGGER = LoggerFactory.getLogger(DiscoveryMembershipProtocol.class);
private final ConcurrentMap members = new ConcurrentHashMap<>();
private final AtomicBoolean started = new AtomicBoolean();
private NodeDiscoveryService discoveryService;
public DiscoveryMembershipProtocol() {}
public DiscoveryMembershipProtocol(final Config config) {
this(config.members);
}
public DiscoveryMembershipProtocol(final Map members) {
this.members.putAll(members);
}
@Override
public Set getMembers() {
return new HashSet<>(members.values());
}
@Override
public Member getMember(final MemberId memberId) {
return members.get(memberId);
}
@Override
public CompletableFuture join(
final BootstrapService bootstrap,
final NodeDiscoveryService discovery,
final Member localMember) {
if (started.compareAndSet(false, true)) {
discovery
.getNodes()
.forEach(
n -> {
final var memberId = MemberId.from(n.id().id());
members.put(memberId, Member.member(memberId, n.address()));
});
members.put(localMember.id(), localMember);
post(new GroupMembershipEvent(GroupMembershipEvent.Type.MEMBER_ADDED, localMember));
discoveryService = discovery;
discoveryService.addListener(this);
LOGGER.info("Started discovery membership protocol with members [{}]", members);
}
return CompletableFuture.completedFuture(null);
}
@Override
public CompletableFuture leave(final Member localMember) {
if (started.compareAndSet(true, false)) {
LOGGER.info("Stopped discovery membership protocol");
discoveryService.removeListener(this);
members.clear();
}
return CompletableFuture.completedFuture(null);
}
@Override
public void event(final NodeDiscoveryEvent event) {
final var node = event.node();
final var memberId = MemberId.from(node.id().id());
final var member = Member.member(memberId, node.address());
if (event.type() == NodeDiscoveryEvent.Type.JOIN) {
if (members.put(memberId, member) == null) {
post(new GroupMembershipEvent(GroupMembershipEvent.Type.MEMBER_ADDED, member));
}
} else if (event.type() == NodeDiscoveryEvent.Type.LEAVE) {
if (members.remove(memberId) != null) {
post(new GroupMembershipEvent(GroupMembershipEvent.Type.MEMBER_REMOVED, member));
}
}
}
@Override
public GroupMembershipProtocolConfig config() {
return new Config(new HashMap<>(members));
}
public static final class Config extends GroupMembershipProtocolConfig {
private final Map members;
public Config(final Map members) {
this.members = members;
}
public Map members() {
return members;
}
@Override
public GroupMembershipProtocol.Type getType() {
return new Type();
}
}
private static final class Type implements GroupMembershipProtocol.Type {
@Override
public String name() {
return "memory";
}
@Override
public GroupMembershipProtocol newProtocol(
final Config config, final String actorSchedulerName) {
return new DiscoveryMembershipProtocol(config);
}
}
}