All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.scalecube.services.discovery.ScalecubeServiceDiscovery Maven / Gradle / Ivy

package io.scalecube.services.discovery;

import static io.scalecube.services.discovery.ClusterMetadataDecoder.decodeMetadata;

import io.scalecube.cluster.Cluster;
import io.scalecube.cluster.ClusterConfig;
import io.scalecube.cluster.Member;
import io.scalecube.services.ServiceEndpoint;
import io.scalecube.services.discovery.api.DiscoveryConfig;
import io.scalecube.services.discovery.api.DiscoveryEvent;
import io.scalecube.services.discovery.api.ServiceDiscovery;
import io.scalecube.services.registry.api.ServiceRegistry;
import io.scalecube.transport.Address;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import reactor.core.publisher.DirectProcessor;
import reactor.core.publisher.Flux;
import reactor.core.publisher.FluxSink;
import reactor.core.publisher.Mono;

import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;

public class ScalecubeServiceDiscovery implements ServiceDiscovery {

  public static final String SERVICE_METADATA = "service";

  private ClusterConfig.Builder clusterConfig = ClusterConfig.builder();

  private static final Logger LOGGER = LoggerFactory.getLogger(ServiceDiscovery.class);

  private ServiceRegistry serviceRegistry;

  private Cluster cluster;

  private ServiceEndpoint endpoint;

  private final DirectProcessor subject = DirectProcessor.create();
  private final FluxSink sink = subject.serialize().sink();

  private enum DiscoveryType {
    ADDED, REMOVED, DISCOVERED;
  }

  @Override
  public Address address() {
    return cluster.address();
  }

  @Override
  public ServiceEndpoint endpoint() {
    return this.endpoint;
  }

  @Override
  public Mono start(DiscoveryConfig config) {
    configure(config);

    clusterConfig.addMetadata(this.serviceRegistry.listServiceEndpoints().stream()
        .collect(Collectors.toMap(ClusterMetadataDecoder::encodeMetadata, service -> SERVICE_METADATA)));
    CompletableFuture promise = Cluster.join(clusterConfig.build())
        .whenComplete((success, error) -> {
          if (error == null) {
            this.cluster = success;
            this.init(this.cluster);
          }
        });

    return Mono.fromFuture(promise).map(mapper -> this);
  }

  @Override
  public Flux listen() {
    return Flux.fromIterable(serviceRegistry.listServiceEndpoints())
        .map(DiscoveryEvent::registered)
        .concatWith(subject);
  }

  @Override
  public Mono shutdown() {
    sink.complete();
    return Mono.fromFuture(cluster.shutdown());
  }

  private void configure(DiscoveryConfig config) {
    this.serviceRegistry = config.serviceRegistry();
    this.endpoint = config.endpoint();

    if (config.seeds() != null) {
      clusterConfig.seedMembers(config.seeds());
    }

    if (config.port() != null) {
      clusterConfig.port(config.port());
    }

    if (config.tags() != null) {
      clusterConfig.metadata(config.tags());
    }
  }

  private void init(Cluster cluster) {
    loadClusterServices(cluster);
    listenCluster(cluster);
  }

  private void listenCluster(Cluster cluster) {
    cluster.listenMembership().subscribe(event -> {
      if (event.isAdded()) {
        loadMemberServices(DiscoveryType.ADDED, event.member());
      } else if (event.isRemoved()) {
        loadMemberServices(DiscoveryType.REMOVED, event.member());
      }
    });
  }

  private void loadClusterServices(Cluster cluster) {
    cluster.otherMembers().forEach(member -> {
      loadMemberServices(DiscoveryType.DISCOVERED, member);
    });
  }

  private void loadMemberServices(DiscoveryType type, Member member) {
    member.metadata().entrySet().stream()
        .filter(entry -> SERVICE_METADATA.equals(entry.getValue()))
        .forEach(entry -> {
          ServiceEndpoint serviceEndpoint = decodeMetadata(entry.getKey());
          if (serviceEndpoint == null) {
            return;
          }

          LOGGER.debug("Member: {} is {} : {}", member, type, serviceEndpoint);
          if ((type.equals(DiscoveryType.ADDED) || type.equals(DiscoveryType.DISCOVERED))
              && (this.serviceRegistry.registerService(serviceEndpoint))) {

            LOGGER.info("Service Reference was ADDED since new Member has joined the cluster {} : {}",
                member, serviceEndpoint);

            DiscoveryEvent registrationEvent = DiscoveryEvent.registered(serviceEndpoint);
            LOGGER.debug("Publish registered: " + registrationEvent);
            sink.next(registrationEvent);

          } else if (type.equals(DiscoveryType.REMOVED)
              && (this.serviceRegistry.unregisterService(serviceEndpoint.id()) != null)) {

            LOGGER.info("Service Reference was REMOVED since Member have left the cluster {} : {}",
                member, serviceEndpoint);

            DiscoveryEvent registrationEvent = DiscoveryEvent.unregistered(serviceEndpoint);
            LOGGER.debug("Publish unregistered: " + registrationEvent);
            sink.next(registrationEvent);
          }
        });
  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy