Introduction to the

Eureka is a service registration and discovery framework developed by Netflix. Spring Cloud integrates Eureka as its default and recommended service registry component and provides out-of-the-box support. Eureka consists of two components: Eureka Client and Eureka Server.

architecture

Eureka Server: node of the registry cluster

Eureka Client: Indicates the Eureka Client

Us-east-xx: indicates the area where a node resides

Application Service: Indicates the Service provider

Application Client: Represents the service caller

The characteristics of

Eureka registries guarantee AP, availability, and fault tolerance of partitions, not strong consistency. Therefore, Eureka can cope well with the situation that some nodes lose contact due to network failure. Different from Eureka, ZooKeeper guarantees CP that when the master node loses contact with other nodes due to network failure, the remaining nodes will conduct “election” for a long time, during which the ZK cluster is unavailable.

Registration services are more about availability, and we can accept that consistency will not be achieved in the short term

To review the CAP principle in distributed systems:

Consistency (C) : Data is updated consistently and all data changes are synchronized.

Availability (A) : As long as A user request is received, it must be able to respond.

Partition tolerance (P) : Communication between partitions of a distributed system may fail, which is inevitable.

Some concepts in Eureka

  • Register: registers the service. When the Eureka Client registers with the Eureka Server, it provides its own metadata, such as IP, port, health indicators, and so on.

  • Renew: Service renewal. The Eureka Client sends a heartbeat message to the Eureka Server every 30 seconds to renew the contract, indicating that the Client is still online.

  • Fetch Registries: Fetch registration list information. The Eureka Client obtains registry information from the Eureka Server and caches it locally. Clients use this information to find other services to make remote calls. The registry information is updated periodically (30s).

  • Cancel: The service is offline. The Eureka Client sends a cancellation request to the Eureka server when the program is shut down. After the request is sent, the client instance information is removed from the server’s instance registry. This referral request is not automatically completed and the following interface is proactively invoked:

    DiscoveryManager.getInstance().shutdownComponent();
    Copy the code
  • Evict: Service culling. By default, if the Eureka Client does not send a service renewal message, that is, heartbeat message, to the Eureka Server in the 90s, the Eureka Server will delete the service instance from the registration list.

  • Peer Node: Nodes in the Eureka Server cluster are Peer regardless of primary and secondary nodes.

Eureka Server source code analysis

The following mainly involves the realization of Server startup, cluster node discovery and information synchronization among nodes in Eureka Server cluster.

Loading way

To set up Eureka, you only need to introduce this dependency:

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId>
        </dependency>
Copy the code

(1) The spring.factories configuration file is stored under the meta-inf package:

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ org.springframework.cloud.netflix.eureka.server.EurekaServerAutoConfiguration
Copy the code

The Eureka Server is loaded in The AutoConfiguration mode.

Condition of EurekaServerAutoConfiguration class appeared on the annotation of comments:

@ConditionalOnBean(EurekaServerMarkerConfiguration.Marker.class)
Copy the code

(2) in addition, Eureka Server create need @ EnableEurekaServer annotations, the annotations of the role is to introduce EurekaServerMarkerConfiguration class. EurekaServerMarkerConfiguration class creates the EurekaServerMarkerConfiguration. Marker type of bean.

Thus, when we introduce the Spring-Cloud-starter-Netflix-Eureka-Server package and add the @enableEurekaserver annotation, Spring Boot can automatically configure eureka Server.

EurekaServerAutoConfiguration

Use to add Eureka Server-related function beans to the Spring container for assembly.

@Configuration
@Import(EurekaServerInitializerConfiguration.class)
@ConditionalOnBean(EurekaServerMarkerConfiguration.Marker.class)
@EnableConfigurationProperties({ EurekaDashboardProperties.class, InstanceRegistryProperties.class })
@PropertySource("classpath:/eureka/server.properties")
public class EurekaServerAutoConfiguration extends WebMvcConfigurerAdapter {

  	// Extract only key code snippets

  	// Register a namedFeatures named Eureka Server
	@Bean
	public HasFeatures eurekaServerFeature(a) {
		return HasFeatures.namedFeature("Eureka Server",
				EurekaServerAutoConfiguration.class);
	}

  	// Eureka. server prefix configuration bean
	@Configuration
	protected static class EurekaServerConfigBeanConfiguration {
		@Bean
		@ConditionalOnMissingBean
		public EurekaServerConfig eurekaServerConfig(EurekaClientConfig clientConfig) {
			EurekaServerConfigBean server = new EurekaServerConfigBean();
			if (clientConfig.shouldRegisterWithEureka()) {
				// Set a sensible default if we are supposed to replicate
				server.setRegistrySyncRetries(5);
			}
			returnserver; }}// EurekaController is enabled by default and provides an interface for obtaining Eureka Server information
	@Bean
	@ConditionalOnProperty(prefix = "eureka.dashboard", name = "enabled", matchIfMissing = true)
	public EurekaController eurekaController(a) {
		return new EurekaController(this.applicationInfoManager);
	}

  	// Peer Eureka Server instance registry
	@Bean
	public PeerAwareInstanceRegistry peerAwareInstanceRegistry( ServerCodecs serverCodecs) {
		this.eurekaClient.getApplications(); // force initialization
		return new InstanceRegistry(this.eurekaServerConfig, this.eurekaClientConfig,
				serverCodecs, this.eurekaClient,
				this.instanceRegistryProperties.getExpectedNumberOfRenewsPerMin(),
				this.instanceRegistryProperties.getDefaultOpenForTrafficCount());
	}

  	// PeerEureka Server node collection, i.e. nodes in the same cluster, PeerEurekaNodes maintains a list of peer nodes
	@Bean
	@ConditionalOnMissingBean
	public PeerEurekaNodes peerEurekaNodes(PeerAwareInstanceRegistry registry, ServerCodecs serverCodecs) {
		return new RefreshablePeerEurekaNodes(registry, this.eurekaServerConfig,
				this.eurekaClientConfig, serverCodecs, this.applicationInfoManager);
	}
	
  	/** If eureka.client. use-dns-for-urls == false and one of the following configurations changes, you can fetch fetch from eureka.client. use-dns-for-urls == false Update peers * eureka. Client. The availability - zones * eureka. Client. The region * eureka. Client. The service - url. < zone > * /
	static class RefreshablePeerEurekaNodes extends PeerEurekaNodes
			implements ApplicationListener<EnvironmentChangeEvent> {

		public RefreshablePeerEurekaNodes(
				final PeerAwareInstanceRegistry registry,
				final EurekaServerConfig serverConfig,
				final EurekaClientConfig clientConfig, 
				final ServerCodecs serverCodecs,
				final ApplicationInfoManager applicationInfoManager) {
			super(registry, serverConfig, clientConfig, serverCodecs, applicationInfoManager);
		}

		@Override
		public void onApplicationEvent(final EnvironmentChangeEvent event) {
			if(shouldUpdate(event.getKeys())) { updatePeerEurekaNodes(resolvePeerUrls()); }}/* * Check whether specific properties have changed. */
		protected boolean shouldUpdate(final Set<String> changedKeys) {
			assertchangedKeys ! =null;
			
			// if eureka.client.use-dns-for-fetching-service-urls is true, then
			// service-url will not be fetched from environment.
			if (clientConfig.shouldUseDnsForFetchingServiceUrls()) {
				return false;
			}
			
			if (changedKeys.contains("eureka.client.region")) {
				return true;
			}
			
			for (final String key : changedKeys) {
				// property keys are not expected to be null.
				if (key.startsWith("eureka.client.service-url.") ||
					key.startsWith("eureka.client.availability-zones.")) {
					return true; }}return false; }}// Eureka Server context Assemble the EurekaServerConfig, PeerAwareInstanceRegistry, PeerEurekaNodes, ApplicationInfoManager, ServerCodecs and provide the getter
	@Bean
	public EurekaServerContext eurekaServerContext(ServerCodecs serverCodecs, PeerAwareInstanceRegistry registry, PeerEurekaNodes peerEurekaNodes) {
		return new DefaultEurekaServerContext(this.eurekaServerConfig, serverCodecs,
				registry, peerEurekaNodes, this.applicationInfoManager);
	}

  	// EurekaServerBootstrap is the glue code for spring-cloud and native eureka. This bootstrap enables eureka Server to run in Embedded Tomcat. Such will be invoked in the EurekaServerInitializerConfiguration, had started.
	@Bean
	public EurekaServerBootstrap eurekaServerBootstrap(PeerAwareInstanceRegistry registry, EurekaServerContext serverContext) {
		return new EurekaServerBootstrap(this.applicationInfoManager,
				this.eurekaClientConfig, this.eurekaServerConfig, registry,
				serverContext);
	}

  	Eureka uses the Jersey framework to implement external restFul interfaces
	/** * Register the Jersey filter */
	@Bean
	public FilterRegistrationBean jerseyFilterRegistration( javax.ws.rs.core.Application eurekaJerseyApp) {
		FilterRegistrationBean bean = new FilterRegistrationBean();
		bean.setFilter(new ServletContainer(eurekaJerseyApp));
		bean.setOrder(Ordered.LOWEST_PRECEDENCE);
		bean.setUrlPatterns(
				Collections.singletonList(EurekaConstants.DEFAULT_PREFIX + "/ *"));

		return bean;
	}

  	/ / register httpTraceFilter
	@Bean
	public FilterRegistrationBean traceFilterRegistration(
			@Qualifier("httpTraceFilter") Filter filter) {
		FilterRegistrationBean bean = new FilterRegistrationBean();
		bean.setFilter(filter);
		bean.setOrder(Ordered.LOWEST_PRECEDENCE - 10);
		returnbean; }}Copy the code

EurekaServerInitializerConfiguration

This class is the Eureka Server program execution entry. IsAutoStartup returns ture, and the start() method of the class is automatically executed after the Spring container is initialized. The start() method opens a new thread to perform the initialization action.

@Configuration
public class EurekaServerInitializerConfiguration
		implements ServletContextAware.SmartLifecycle.Ordered {

  	// Extract only key code snippets
  
  	Start Eureka Server
	@Override
	public void start(a) {
		new Thread(new Runnable() {
			@Override
			public void run(a) {
				try {
					//TODO: is this class even needed now?
					// Initialize the context
                  	eurekaServerBootstrap.contextInitialized(EurekaServerInitializerConfiguration.this.servletContext);
					log.info("Started Eureka Server");

                  	// Publish EurekaServer registration event
					publish(new EurekaRegistryAvailableEvent(getEurekaServerConfig()));
                  	// Change the running state
					EurekaServerInitializerConfiguration.this.running = true;
                  	// Send the Eureka Start event
					publish(new EurekaServerStartedEvent(getEurekaServerConfig()));
				}
				catch (Exception ex) {
					// Help!
					log.error("Could not initialize Eureka servlet context", ex); } } }).start(); }}Copy the code

EurekaServerBootstrap

As mentioned earlier, the EurekaServer bootstrap code is copied from the EurekaBootStrap. Change the ServletContextListener mode to start during the lifecycle of the Spring container.

public class EurekaServerBootstrap {

  	// Extract only key code snippets

  	// Configuration information required for running Eureka Server
  	protected EurekaServerConfig eurekaServerConfig;
  	Class that registers with Eureka Server and initializes information needed to be discovered by other components
	protected ApplicationInfoManager applicationInfoManager;
  	// Configures the Eureka Client to register the instance with the Eureka Server
	protected EurekaClientConfig eurekaClientConfig;
  	// The instance registry
	protected PeerAwareInstanceRegistry registry;
  	// EurekaServer context
	protected volatile EurekaServerContext serverContext;
  
  	/ / in the above EurekaServerInitializerConfiguration# start () is invoked
	public void contextInitialized(ServletContext context) {
		try {
          	// Initialize the Eureka environment
			initEurekaEnvironment();
          	// Initialize the EurekaServer context
			initEurekaServerContext();

          	// Set the EurekaServer context to the Servlet context
			context.setAttribute(EurekaServerContext.class.getName(), this.serverContext);
		}
		catch (Throwable e) {
			log.error("Cannot bootstrap eureka server :", e);
			throw new RuntimeException("Cannot bootstrap eureka server :", e); }}/ / EurekaServerInitializerConfiguration stop () this method is called when they destroyed the context and environment
	public void contextDestroyed(ServletContext context) {
		try {
			log.info("Shutting down Eureka Server..");
			context.removeAttribute(EurekaServerContext.class.getName());

			destroyEurekaServerContext();
			destroyEurekaEnvironment();

		}
		catch (Throwable e) {
			log.error("Error shutting down eureka", e);
		}
		log.info("Eureka Service is now shutdown...");
	}

  	// Initialize the Eureka environment variables, that is, read and set the datacenter and Enviroment configuration parameters from the configuration file
	protected void initEurekaEnvironment(a) throws Exception {
		log.info("Setting the eureka configuration..");

		String dataCenter = ConfigurationManager.getConfigInstance()
				.getString(EUREKA_DATACENTER);
		if (dataCenter == null) {
			log.info(
					"Eureka data center value eureka.datacenter is not set, defaulting to default");
			ConfigurationManager.getConfigInstance()
					.setProperty(ARCHAIUS_DEPLOYMENT_DATACENTER, DEFAULT);
		}
		else {
			ConfigurationManager.getConfigInstance()
					.setProperty(ARCHAIUS_DEPLOYMENT_DATACENTER, dataCenter);
		}
		String environment = ConfigurationManager.getConfigInstance()
				.getString(EUREKA_ENVIRONMENT);
		if (environment == null) {
			ConfigurationManager.getConfigInstance()
					.setProperty(ARCHAIUS_DEPLOYMENT_ENVIRONMENT, TEST);
			log.info(
					"Eureka environment value eureka.environment is not set, defaulting to test");
		}
		else{ ConfigurationManager.getConfigInstance() .setProperty(ARCHAIUS_DEPLOYMENT_ENVIRONMENT, environment); }}// Initialize the EurekaServer context
	protected void initEurekaServerContext(a) throws Exception {
		// For backward compatibility
		JsonXStream.getInstance().registerConverter(new V1AwareInstanceInfoConverter(),
				XStream.PRIORITY_VERY_HIGH);
		XmlXStream.getInstance().registerConverter(new V1AwareInstanceInfoConverter(),
				XStream.PRIORITY_VERY_HIGH);

		if (isAws(this.applicationInfoManager.getInfo())) {
			this.awsBinder = new AwsBinderDelegate(this.eurekaServerConfig,
					this.eurekaClientConfig, this.registry, this.applicationInfoManager);
			this.awsBinder.start();
		}
      	// This is AWS logic

      	/ / EurekaServerContextHolder holders is a static EurekaServer context, singleton. EurekaServerContextHolder here is initialized, the EurekaServerContext is assigned to the Holder.
		EurekaServerContextHolder.initialize(this.serverContext);

		log.info("Initialized server context");

		// Copy registry from neighboring eureka node
      	// Copy all registration information from adjacent nodes in Eureka Server cluster
		int registryCount = this.registry.syncUp();
      	// Set the state to UP to allow services under the zone to register with it
		this.registry.openForTraffic(this.applicationInfoManager, registryCount);

		// Register all monitoring statistics.
      	// Register all monitoring statisticsEurekaMonitors.registerAllStats(); }}Copy the code

PeerAwareInstanceRegistryImpl

Eureka Server startup, EurekaServerBootstrap call PeerAwareInstanceRegistryImpl# syncUp (), the registration information from the adjacent nodes synchronization service.

public class PeerAwareInstanceRegistryImpl extends AbstractInstanceRegistry implements PeerAwareInstanceRegistry {...// Copy the complete service information from the adjacent DS node and register it in its own registry
	/** * Populates the registry information from a peer eureka node. This * operation fails over to other nodes until the list is exhausted if the * communication fails. */
    @Override
    public int syncUp(a) {
        // Copy entire entry from neighboring DS node
        int count = 0;

      	/ / maximum synchronous retries serverConfig. GetRegistrySyncRetries ()
        for (int i = 0; ((i < serverConfig.getRegistrySyncRetries()) && (count == 0)); i++) {
            if (i > 0) {
                try {
                  	/ / synchronization failure after the retry interval serverConfig. GetRegistrySyncRetryWaitMs ()
                    Thread.sleep(serverConfig.getRegistrySyncRetryWaitMs());
                } catch (InterruptedException e) {
                    logger.warn("Interrupted during registry transfer..");
                    break; }}// first get all registered service information from EurekaClient#getApplications()
          	// Here you can see that all registration information is encapsulated in Applications, which contains all services; Application encapsulates the registration information of a service. Application contains all instances of the service. InstanceInfo encapsulates the instance information.
            Applications apps = eurekaClient.getApplications();
            for (Application app : apps.getRegisteredApplications()) {
                for (InstanceInfo instance : app.getInstances()) {
                    try {
                        if (isRegisterable(instance)) {
                          	// Register the service instance
                            register(instance, instance.getLeaseInfo().getDurationInSecs(), true); count++; }}catch (Throwable t) {
                        logger.error("During DS init copy", t); }}}}return count;
    }

  	// Sets the state of the instance, indicating whether it is ready to receive communication, and notifies other listeners of state change events
    @Override
    public void openForTraffic(ApplicationInfoManager applicationInfoManager, int count) {
        // Renewals happen every 30 seconds and for a minute it should be a factor of 2.
        this.expectedNumberOfRenewsPerMin = count * 2;
        this.numberOfRenewsPerMinThreshold =
                (int) (this.expectedNumberOfRenewsPerMin * serverConfig.getRenewalPercentThreshold());
        logger.info("Got {} instances from neighboring DS node", count);
        logger.info("Renew threshold is: {}", numberOfRenewsPerMinThreshold);
        this.startupTime = System.currentTimeMillis();
        if (count > 0) {
            this.peerInstancesTransferEmptyOnStartup = false;
        }
        DataCenterInfo.Name selfName = applicationInfoManager.getInfo().getDataCenterInfo().getName();
        boolean isAws = Name.Amazon == selfName;
        if (isAws && serverConfig.shouldPrimeAwsReplicaConnections()) {
            logger.info("Priming AWS connections for all replicas..");
            primeAwsReplicas(applicationInfoManager);
        }
        logger.info("Changing status to UP");
        applicationInfoManager.setInstanceStatus(InstanceStatus.UP);
      	// postInit enables the scheduled task of node elimination
        super.postInit(); }}Copy the code

EurekaClient

In the above syncUp method calls the eurekaClient. GetApplications (), so where is eurekaClient injection?

The spring-cloud-netflix-eureka-client jar is used in the spring-cloud-starter-netflix-eureka-server jar. The spring-cloud-netflix-Eureka-client jar jar is used in the Spring-. CloudEurekaClient is a subclass of EurekaClient. CloudEurekaClient is a subclass of EurekaClient.

Regardless of when data is written to eurekaClient, we can guess that each Eureka Server actually has a Eureka Client, which obtains service information from other Eureka Server nodes through the built-in Eureka Client.

PeerEurekaNodes

Peer Eureka Server node collection, this class is an abstraction of Eureka Server cluster nodes. After this node is started, information about all adjacent nodes of the current node is periodically maintained. PeerEurekaNode is an abstraction of a node.

EurekaServerContext is injected into the EurekaServerContext bootstrap. Defined in EurekaServerAutoConfiguration EurekaServerContext type of bean – its implementation class DefaultEurekaServerContext. DefaultEurekaServerContext @ PostConstruct method called the peerEurekaNodes. The start ().

public class PeerEurekaNodes {

  	// Extract only key code snippets

    protected final PeerAwareInstanceRegistry registry;
  	// Cluster node collection
    private volatile List<PeerEurekaNode> peerEurekaNodes = Collections.emptyList();
  	// Cluster node URL collection
    private volatile Set<String> peerEurekaNodeUrls = Collections.emptySet();
  	// Scheduled task thread pool
    private ScheduledExecutorService taskExecutor;

  	// Start the scheduled task thread to updatePeerEurekaNodes
    public void start(a) {
        taskExecutor = Executors.newSingleThreadScheduledExecutor(
                new ThreadFactory() {
                    @Override
                    public Thread newThread(Runnable r) {
                        Thread thread = new Thread(r, "Eureka-PeerNodesUpdater");
                        thread.setDaemon(true);
                        returnthread; }});try {
          	// Update node information
            updatePeerEurekaNodes(resolvePeerUrls());
            Runnable peersUpdateTask = new Runnable() {
                @Override
                public void run(a) {
                    try {
                        updatePeerEurekaNodes(resolvePeerUrls());
                    } catch (Throwable e) {
                        logger.error("Cannot update the replica Nodes", e); }}}; taskExecutor.scheduleWithFixedDelay( peersUpdateTask, serverConfig.getPeerEurekaNodesUpdateIntervalMs(), serverConfig.getPeerEurekaNodesUpdateIntervalMs(), TimeUnit.MILLISECONDS ); }catch (Exception e) {
            throw new IllegalStateException(e);
        }
        for (PeerEurekaNode node : peerEurekaNodes) {
            logger.info("Replica node URL: {}", node.getServiceUrl()); }}// Close the cluster
    public void shutdown(a) {
        taskExecutor.shutdown();
        List<PeerEurekaNode> toRemove = this.peerEurekaNodes;

      	// Clear the list of nodes and urls
        this.peerEurekaNodes = Collections.emptyList();
        this.peerEurekaNodeUrls = Collections.emptySet();

      	// Shut down each node
        for(PeerEurekaNode node : toRemove) { node.shutDown(); }}// Get the urls of all nodes in the cluster except the current one
    /**
     * Resolve peer URLs.
     *
     * @return peer URLs with node's own URL filtered out
     */
    protected List<String> resolvePeerUrls(a) {
        InstanceInfo myInfo = applicationInfoManager.getInfo();
        String zone = InstanceInfo.getZone(clientConfig.getAvailabilityZones(clientConfig.getRegion()), myInfo);
        List<String> replicaUrls = EndpointUtils
                .getDiscoveryServiceUrls(clientConfig, zone, new EndpointUtils.InstanceInfoBasedUrlRandomizer(myInfo));

        int idx = 0;
        while (idx < replicaUrls.size()) {
            if (isThisMyUrl(replicaUrls.get(idx))) {
                replicaUrls.remove(idx);
            } else{ idx++; }}return replicaUrls;
    }

    /**
     * Given new set of replica URLs, destroy {@link PeerEurekaNode}s no longer available, and
     * create new ones.
     *
     * @param newPeerUrls peer node URLs; this collection should have local node's URL filtered out
     */
    protected void updatePeerEurekaNodes(List<String> newPeerUrls) {
        if (newPeerUrls.isEmpty()) {
            logger.warn("The replica size seems to be empty. Check the route 53 DNS Registry");
            return;
        }

        Set<String> toShutdown = new HashSet<>(peerEurekaNodeUrls);
        toShutdown.removeAll(newPeerUrls); // The node to remove and stop
        Set<String> toAdd = new HashSet<>(newPeerUrls);
        toAdd.removeAll(peerEurekaNodeUrls); // The node to be added

      	// No URL to remove or add is returned directly
        if (toShutdown.isEmpty() && toAdd.isEmpty()) { // No change
            return;
        }

        // Remove peers no long available
        List<PeerEurekaNode> newNodeList = new ArrayList<>(peerEurekaNodes);

      	// Remove and stop
        if(! toShutdown.isEmpty()) { logger.info("Removing no longer available peer nodes {}", toShutdown);
            int i = 0;
            while (i < newNodeList.size()) {
                PeerEurekaNode eurekaNode = newNodeList.get(i);
                if (toShutdown.contains(eurekaNode.getServiceUrl())) {
                    newNodeList.remove(i);
                    eurekaNode.shutDown();
                } else{ i++; }}}// Add a new node
        // Add new peers
        if(! toAdd.isEmpty()) { logger.info("Adding new peer nodes {}", toAdd);
            for(String peerUrl : toAdd) { newNodeList.add(createPeerEurekaNode(peerUrl)); }}this.peerEurekaNodes = newNodeList;
        this.peerEurekaNodeUrls = new HashSet<>(newPeerUrls);
    }

  	// Construct PeerEurekaNode according to the URL
    protected PeerEurekaNode createPeerEurekaNode(String peerEurekaNodeUrl) {
      	// Create an HTTP client to communicate with peers and synchronize data
        HttpReplicationClient replicationClient = JerseyReplicationClient.createReplicationClient(serverConfig, serverCodecs, peerEurekaNodeUrl);
        String targetHost = hostFromUrl(peerEurekaNodeUrl);
        if (targetHost == null) {
            targetHost = "host";
        }
      	// Returns the constructed node
        return newPeerEurekaNode(registry, targetHost, peerEurekaNodeUrl, replicationClient, serverConfig); }}Copy the code

PeerEurekaNode

It can be seen from the above that Eureka uses PeerEurekaNode to represent peer nodes and uses HTTP clients to communicate between nodes. Next, look at PeerEurekaNode to see how information is synchronized between cluster nodes.

public class PeerEurekaNode {

  	// Extract only key code snippets

  	// Batch task allocator
	private final TaskDispatcher<String, ReplicationTask> batchingDispatcher;
  	// Non-batch task allocator
	private final TaskDispatcher<String, ReplicationTask> nonBatchingDispatcher;

  	// When a new service registers with the current node, use this method to synchronize the service registration information to the peer node
    public void register(final InstanceInfo info) throws Exception {
        long expiryTime = System.currentTimeMillis() + getLeaseRenewalOf(info);
      	// Add a synchronization task to register a new service in the task allocator
        batchingDispatcher.process(
                taskId("register", info),
                new InstanceReplicationTask(targetHost, Action.Register, info, null.true) {
                    public EurekaHttpResponse<Void> execute(a) {
                        return replicationClient.register(info);
                    }
                },
                expiryTime
        );
    }

  	// This method is used to synchronize information to the peer node when services are cancelled (offline)
    public void cancel(final String appName, final String id) throws Exception {
        long expiryTime = System.currentTimeMillis() + maxProcessingDelayMs;
      	// Add and cancel tasks
        batchingDispatcher.process(
                taskId("cancel", appName, id),
                new InstanceReplicationTask(targetHost, Action.Cancel, appName, id) {
                    @Override
                    public EurekaHttpResponse<Void> execute(a) {
                        return replicationClient.cancel(appName, id);
                    }

                    @Override
                    public void handleFailure(int statusCode, Object responseEntity) throws Throwable {
                        super.handleFailure(statusCode, responseEntity);
                        if (statusCode == 404) {
                            logger.warn("{}: missing entry.", getTaskName());
                        }
                    }
                },
                expiryTime
        );
    }

    // Heartbeat synchronization task. The current node has services to send heartbeat renewal and synchronize the heartbeat information to the peer node
    public void heartbeat(final String appName, final String id,
                          final InstanceInfo info, final InstanceStatus overriddenStatus,
                          boolean primeConnection) throws Throwable {
        if (primeConnection) {
            // We do not care about the result for priming request.
            replicationClient.sendHeartBeat(appName, id, info, overriddenStatus);
            return;
        }
        ReplicationTask replicationTask = new InstanceReplicationTask(targetHost, Action.Heartbeat, info, overriddenStatus, false) {
            @Override
            public EurekaHttpResponse<InstanceInfo> execute(a) throws Throwable {
                return replicationClient.sendHeartBeat(appName, id, info, overriddenStatus);
            }

            @Override
            public void handleFailure(int statusCode, Object responseEntity) throws Throwable {
                super.handleFailure(statusCode, responseEntity);
                if (statusCode == 404) {
                    logger.warn("{}: missing entry.", getTaskName());
                    if(info ! =null) {
                        logger.warn("{}: cannot find instance id {} and hence replicating the instance with status {}", getTaskName(), info.getId(), info.getStatus()); register(info); }}else if (config.shouldSyncWhenTimestampDiffers()) {
                    InstanceInfo peerInstanceInfo = (InstanceInfo) responseEntity;
                    if(peerInstanceInfo ! =null) { syncInstancesIfTimestampDiffers(appName, id, info, peerInstanceInfo); }}}};long expiryTime = System.currentTimeMillis() + getLeaseRenewalOf(info);
        batchingDispatcher.process(taskId("heartbeat", info), replicationTask, expiryTime); }}Copy the code

ApplicationResource

This class is the interface Controller for service registration and query.

addInstance() // Register information about the specified instance
getInstanceInfo() // Obtain information about the specified application instance
getApplication // Obtain the specified application information
Copy the code

summary

When Eureka Server is started, it obtains the service registration information from the cluster DS node and registers it in its registry. Then, it sets its state to UP and waits for the services in the region to register, renew, and query. Then, it starts a service deletion task to periodically clear the heartBeat instances that are not sent to the node within the specified time.

Scheduled tasks are also initiated to maintain adjacent nodes.

Eureka Client source code analysis

Loading way

As mentioned above, spring-cloud-Netflix-Eureka-client is loaded through the SpringFactoriesLoader. Multiple classes are loaded in this file:

# auto-loaded classes
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.cloud.netflix.eureka.config.EurekaClientConfigServerAutoConfiguration,\
org.springframework.cloud.netflix.eureka.config.EurekaDiscoveryClientConfigServiceAutoConfiguration,\
org.springframework.cloud.netflix.eureka.EurekaClientAutoConfiguration,\
org.springframework.cloud.netflix.ribbon.eureka.RibbonEurekaAutoConfiguration,\
org.springframework.cloud.netflix.eureka.EurekaDiscoveryClientConfiguration
Read the configuration from bootstrap.yml to initialize the context
org.springframework.cloud.bootstrap.BootstrapConfiguration=\
org.springframework.cloud.netflix.eureka.config.EurekaDiscoveryClientConfigServiceBootstrapConfiguration
Copy the code

The problem

(1) Although Eureka can deploy cluster architecture, each Eureka instance in the cluster is peer. Each Eureka instance contains all service registries. When each Eureka instance receives service registration/offline requests, it will be synchronized to other Eureka instances in the cluster to realize cluster data synchronization. In the case of a very large service cluster, the service registry may eventually exceed the memory capacity of a single server.

(2) In July 2018, Netflix officially announced that Eureka 2.0 was out of maintenance.