• SpringCloud-Spring Cloud Context
  • SpringCloud-Eureka service registration
  • SpringCloud-Eureka service discovery
  • Spring Cloud-Eureka Client
  • SpringCloud- Declarative service invocation Feign
  • SpringCloud- load balancer Ribbon
  • SpringCloud- Configuration center Config
  • SpringCloud- Configuration center ZooKeeper
  • SpringCloud- Configuration Center Apollo
  • Springcloud-config configuration center principle
  • SpringCloud- Circuit breaker Hystrix

Previous demos have shown how to use SOFABoot to integrate the Spring Cloud Netflix Eureka component. This article will first analyze the working principle of Eureka Client.

Netflix and SpringCloud

The Spring-Cloud-Commons module is the specification definition of Spring in the distributed domain (service discovery, service registration, circuit breakers, load balancing). Spring-cloud-netflix is based on a concrete implementation of this specification, and various components in Netflix OSS also implement the Commons specification. The relationship is as follows:

Spring Cloud Netflix Eureka service discovery principle

Based on the figure above, here is an example of how service discovery in Eureka is implemented. Two key classes for service discovery are provided in Spring Cloud Common: the DiscoveryClient interface and the EnableDiscoveryClient annotation.

DiscoveryClient interface

The chart below shows how SpringCloud integrates with Netflix in service discovery. The DiscoveryClient interface in Spring Cloud Common is implemented in Spring-Cloud-Netflix-Eureka-Client. The implementation class is EurekaDiscoveryClient.

Interface definitions and methods of DiscoveryClient:

/** * DiscoveryClient indicates a common read operation for service discovery, such as Netflix Eureka or Consul.io *@author Spencer Gibb
 */
public interface DiscoveryClient {

	/** * Implementation description *@return the description
	 */
	String description(a);

	/** * Get all ServiceInstances * associated with a specific serviceId@param serviceId the serviceId to query
	 * @return a List of ServiceInstance
	 */
	List<ServiceInstance> getInstances(String serviceId);

	/** * returns all known service ids */
	List<String> getServices(a);
}
Copy the code

EurekaDiscoveryClient implements these methods, but EurekaDiscoveryClient itself does not implement the logic of how to interact with the server, but rather through com.net flix. DiscoveryClient class to complete. So what spring-Cloud-Netflix-Eureka-client does is implement the Spring Cloud Common specification and then wrap Netflix on top of that implementation.

@ EnableDiscoveryClient annotations


EnableDiscoveryClientImportSelector will from the meta-inf/spring. Find out the key in the factories For org. Springframework. Cloud. Client. Discovery. EnableDiscoveryClient class.

For autoRegister:

  • If the auto-registration attribute is true, one more class will be added to the list of found classes: AutoServiceRegistrationConfiguration, AutoServiceRegistrationConfiguration internal use @ EnableConfigurationProperties (AutoServiceRegistrationProperties. Class) Trigger structure AutoServiceRegistrationProperties the bean. Like Eureka, Nacos, Their automation configuration class use @ ConditionalOnBean AutoServiceRegistrationProperties. Class (a) to ensure that existing AutoServiceRegistrationProperties this bean Exists only when autoService stration is constructed for registration.
  • If the auto-registration property is false, add an PropertySource to the Environment with the internal configuration item spring.cloud.service-registry.auto-registration.enabled. The value is false (for no tectonic AutoServiceRegistrationProperties. Class). So Eureka wouldn’t register.

The code for this logic is as follows:

Spring-cloud-netflix-eureka-client also provides an annotation of its own, EnableEurekaClient, which works as well as this annotation

Eureka architecture diagram

  • Consumer: The service consumer, the Eureka client role, can pull the information of other registered services from the Eureka Server, so as to find the required service according to the information, and then initiate a remote call.
  • Provider: A service provider, as a Eureka client, can register and update its own information with the Eureka server. Of course, as a Eureka client, it can obtain information about other services from the Server.
  • Eureka Server: Service registry, providing service registration and service discovery functions;
  • Synchronous replication: Synchronize registration service information between Eureka servers to ensure that each server in the cluster can provide complete service information.

Check out the resources for Regin and Availability Zone concepts on AWS

The source code parsing

Reading Configuration Information

Eureka automatic configuration of the Client class is org.springframework.cloud.net flix. Eureka. EurekaClientAutoConfiguration, This is mainly responsible for the initialization of some configuration information services such as DiscoveryClient, EurekaServiceRegistry and other main beans.

Another EurekaDiscoveryClientConfiguration class, responsible for configuration automatic registration and application of health inspection device initialization.

Read eureka. Client. *

@Bean
@ConditionalOnMissingBean(value = EurekaClientConfig.class, search = SearchStrategy.CURRENT)
public EurekaClientConfigBean eurekaClientConfigBean(ConfigurableEnvironment env) {
	EurekaClientConfigBean client = new EurekaClientConfigBean();
  if ("bootstrap".equals(this.env.getProperty("spring.config.name"))) {
    // By default, we will not register during boot, but there will be another opportunity later.
    client.setRegisterWithEureka(false);
  }
  return client;
}
Copy the code

EurekaClientConfigBean encapsulates the configuration information required for the interaction between eureka Client and Eureka Server. For example, the eureka.client.service-url.defaultzone configuration in the previous demo project.

Read eureka. Instance. *

@Bean
@ConditionalOnMissingBean(value = EurekaInstanceConfig.class, search = SearchStrategy.CURRENT)
public EurekaInstanceConfigBean eurekaInstanceConfigBean(InetUtils inetUtils, ManagementMetadataProvider managementMetadataProvider) {
  // The code is long and omitted here
}
Copy the code

The EurekaInstanceConfigBean encapsulates the configuration information of the Eureka client instance and provides basic metadata information for service registration.

Initialize the core component bean

Some core component beans are also instantiated here.

ApplicationInfoManager

  • EurekaClientConfiguration#eurekaApplicationInfoManager
@Bean
@ConditionalOnMissingBean(value = ApplicationInfoManager.class, search = SearchStrategy.CURRENT)
public ApplicationInfoManager eurekaApplicationInfoManager(
EurekaInstanceConfig config) {
  InstanceInfo instanceInfo = new InstanceInfoFactory().create(config);
  return new ApplicationInfoManager(config, instanceInfo);
}
Copy the code
  • RefreshableEurekaClientConfiguration#eurekaApplicationInfoManager
@Bean
@ConditionalOnMissingBean(value = ApplicationInfoManager.class, search = SearchStrategy.CURRENT)
@org.springframework.cloud.context.config.annotation.RefreshScope
@Lazy
public ApplicationInfoManager eurekaApplicationInfoManager(EurekaInstanceConfig config) {
  InstanceInfo instanceInfo = new InstanceInfoFactory().create(config);
  return new ApplicationInfoManager(config, instanceInfo);
}
Copy the code

RefreshScope, when marked by this annotation, will be refreshed dynamically. For dynamic refreshes, classes marked by RefreshScope cannot be final.

ApplicationInfoManager is an application information manager used to manage InstanceInfo and EurekaInstanceConfig of service instances.

DiscoveryClient

@Bean
public DiscoveryClient discoveryClient(EurekaInstanceConfig config, EurekaClient client) {
	return new EurekaDiscoveryClient(config, client);
}
Copy the code

DiscoveryClient, as mentioned earlier, is the client interface used for service discovery in the Spring Cloud. Note that this is the interface provided by SpringCloud, not the classes in Netflix.

EurekaServiceRegistry

@Bean
public EurekaServiceRegistry eurekaServiceRegistry(a) {
	return new EurekaServiceRegistry();
}
Copy the code

EurekaServiceRegistry is an implementation class for Service eregistry. ServiceRegistry SpringCloud provides methods such as registration and logout that allow users to provide custom registration services.

EurekaRegistration

@Bean
	@ConditionalOnBean(AutoServiceRegistrationProperties.class)
	@ConditionalOnProperty(value = "spring.cloud.service-registry.auto-registration.enabled", matchIfMissing = true)
	public EurekaRegistration eurekaRegistration(EurekaClient eurekaClient, CloudEurekaInstanceConfig instanceConfig, ApplicationInfoManager applicationInfoManager, ObjectProvider<HealthCheckHandler> healthCheckHandler) {
		return EurekaRegistration.builder(instanceConfig)
				.with(applicationInfoManager)
				.with(eurekaClient)
				.with(healthCheckHandler)
				.build();
	}
Copy the code

Each ServiceRegistry implementation has its own Registry implementation.

  • ZookeeperRegistration -> ZookeeperServiceRegistry
  • ZookeeperRegistration -> EurekaServiceRegistry
  • ConsulRegistration       -> ConsulServiceRegistry

If you need a custom implementation ServiceRegistry, don’t provide an implementation of Registration either.

Service discovery

Basic situation of the service discovery already mentioned in the above, but with SpingCloud does not provide specific interactions but by com.net flix. Discovery. DiscoveryClient to complete the specific work. So in terms of service discovery we’re going to go straight to this class.

LookopService

public interface LookupService<T> {
    // Get the Application based on the appName registered with the service instance
    Application getApplication(String appName);
    // Returns information about all service instances in the current registry
    Applications getApplications(a);
    // Obtain the service instance information based on the service instance Id
    List<InstanceInfo> getInstancesById(String id);
    /** * Gets the next possible server to process the request from the registry information received from Eureka. *@virtualHostnameThe virtual host name associated with the server. *@secureIndicates whether the request is HTTP or HTTPS * */
    InstanceInfo getNextServerFromEureka(String virtualHostname, boolean secure);
}
Copy the code

The purpose of the LookupService interface is to find active service instances; Altogether, four methods are provided, which are easy to understand. See the comments for what each method does.

EurekaClient

EurekaClient is also an interface that integrates and extends LookupService.

This interface does NOT try to clean up the current client interface for eureka 1.x. Rather it tries to provide an easier transition path from eureka 1.x to eureka 2.x. From this point of view, EurekaClient exists to provide fault tolerance for Eureka1.x upgrade to Eureka 2.x.

EurekaClient extends a number of methods on top of LookupService, as follows:

public interface EurekaClient extends LookupService {
  	// Omit the @deprecated method and the interface method to get service instance information
		// Register the health check handler
    public void registerHealthCheck(HealthCheckHandler healthCheckHandler);
		// Listen for the update of client service information
    public void registerEventListener(EurekaEventListener eventListener);
   	// Cancel the listener
    public boolean unregisterEventListener(EurekaEventListener eventListener);
 		// Get the current health check handler
    public HealthCheckHandler getHealthCheckHandler(a);
		// Close the Eureka client. A deregistration request is also sent to the Eureka server.
    public void shutdown(a);
  	// EurekaClientConfig
    public EurekaClientConfig getEurekaClientConfig(a);
 		// ApplicationInfoManager
    public ApplicationInfoManager getApplicationInfoManager(a);
}
Copy the code

HealthCheckHandler This is used to check the current state of the client, as described later in the heartbeat mechanism.

DiscoveryClient

Com.net flix. Discovery. DiscoveryClient, this class will be done in the constructor of a series of important operations, such as: pull the registry information, service registry, initialize the heartbeat mechanism, cache refresh, on-demand registered timed tasks and so on.

 DiscoveryClient(ApplicationInfoManager applicationInfoManager, 
 								 EurekaClientConfig config, 
                 AbstractDiscoveryClientOptionalArgs args,
                 Provider<BackupRegistry> backupRegistryProvider) {
 // ... 
 }
Copy the code

Several parameters are interpreted as follows:

  • ApplicationInfoManager: Application information manager
  • Config: indicates the configuration information about the interaction between the client and server
  • Args: Filter type provided by the client (support jersey1 and Jersey2), which is later used to build EurekaTransport
  • BackupRegistryProvider: backup registry

Service discovery

The following code snippet, also in the DiscoveryClient constructor, is the logic for pulling registration service information:

if(clientConfig.shouldFetchRegistry() && ! fetchRegistry(false)) {
	fetchRegistryFromBackup();
}
Copy the code

ClientConfig. ShouldFetchRegistry () this method get is the configuration file eureka. Client. The fetch – the value of the registry, the default is true, said from eureka server pull the registry information.

FetchRegistry (Boolean) fetchRegistry(Boolean) fetchRegistry(Boolean) fetchRegistry(Boolean) fetchRegistry Unless there is a problem with reconcilating the eureka server and client registry information, this method only tries to get the full fetch on the first attempt and then increments.

FetchRegistryFromBackup () Alternative if the Eureka server service is unavailable.

The underlying communication implements EurekaTransport

EurekaTransport is an internal class of DiscoveryClient that encapsulates a specific Jersey-based underlying communication implementation.

FetchRegistry

The service registry

The service registration logic is also done in the DiscoveryClient constructor, with the following code snippet:

if (clientConfig.shouldRegisterWithEureka() && clientConfig.shouldEnforceRegistrationAtInit()) {
  try {
    if(! register() ) {throw new IllegalStateException("Registration error at startup. Invalid server response."); }}catch (Throwable th) {
    logger.error("Registration error at startup: {}", th.getMessage());
    throw newIllegalStateException(th); }}Copy the code

Registration with the server must meet the following two conditions: 1. Registration with the server is allowed. 2

boolean register(a) throws Throwable {
  logger.info(PREFIX + "{}: registering service...", appPathIdentifier);
  EurekaHttpResponse<Void> httpResponse;
  try {
  	httpResponse = eurekaTransport.registrationClient.register(instanceInfo);
  } catch (Exception e) {
    logger.warn(PREFIX + "{} - registration failed {}", appPathIdentifier, e.getMessage(), e);
    throw e;
  }
  if (logger.isInfoEnabled()) {
  	logger.info(PREFIX + "{} - registration status: {}", appPathIdentifier, httpResponse.getStatusCode());
  }
  return httpResponse.getStatusCode() == 204;
}
Copy the code

The service is registered with the Eureka Server through the eurekaTransport object based on REST calls.

heartbeat

Initialization of the heartbeat mechanism is also done in the DiscoveryClient constructor. At the end of the DiscoveryClient constructor, there is a method that initializes the scheduled task, including the initialization of the heartbeat.

HeartbeatExecutor Heartbeat thread pool:

heartbeatExecutor = new ThreadPoolExecutor(
                    1, clientConfig.getHeartbeatExecutorThreadPoolSize(), 0, TimeUnit.SECONDS,
                    new SynchronousQueue<Runnable>(),
                    new ThreadFactoryBuilder()
                            .setNameFormat("DiscoveryClient-HeartbeatExecutor-%d")
                            .setDaemon(true)
                            .build()
Copy the code

Scheduler commit cycle execution:

// Heartbeat timer
scheduler.schedule(
                  new TimedSupervisorTask(
                  "heartbeat",
                  scheduler,
                  heartbeatExecutor,
                  renewalIntervalInSecs,
                  TimeUnit.SECONDS,
                  expBackOffBound,
                  new HeartbeatThread()
                  ),
									renewalIntervalInSecs, TimeUnit.SECONDS);
Copy the code

The Timedcontainer is designed to automatically adjust the interval for the periodic task in Eureka. A HeartbeatThread is any thread that executes, and the renew() method is used in the run method.

boolean renew(a) {
  EurekaHttpResponse<InstanceInfo> httpResponse;
  try {
    // Use eurekaTransport to communicate with the server for renewal
    httpResponse = eurekaTransport.registrationClient.sendHeartBeat(instanceInfo.getAppName(), instanceInfo.getId(), instanceInfo, null);
    logger.debug(PREFIX + "{} - Heartbeat status: {}", appPathIdentifier, httpResponse.getStatusCode());
    // 404 Indicates that the current service instance does not exist
    if (httpResponse.getStatusCode() == 404) {
      // Record the heartbeat count
      REREGISTER_COUNTER.increment();
      logger.info(PREFIX + "{} - Re-registering apps/{}", appPathIdentifier, instanceInfo.getAppName());
      long timestamp = instanceInfo.setIsDirtyWithTime();
      // Re-register
      boolean success = register();
      if (success) {
      	instanceInfo.unsetIsDirty(timestamp);
      }
    	return success;
    }
    // 200 is in normal state
    return httpResponse.getStatusCode() == 200;
  } catch (Throwable e) {
    logger.error(PREFIX + "{} - was unable to send heartbeat!", appPathIdentifier, e);
    return false; }}Copy the code

Service offline

Shut down the Eureka client and send an deregistration request to the Eureka server. This method is in the DiscoveryClient#shutdown method.

@PreDestroy
    @Override
    public synchronized void shutdown(a) {
  			// Ensure atomic operation
        if (isShutdown.compareAndSet(false.true)) {
            logger.info("Shutting down DiscoveryClient ...");
            if(statusChangeListener ! =null&& applicationInfoManager ! =null) {
              	// The application manager cancels status listening
                applicationInfoManager.unregisterStatusChangeListener(statusChangeListener.getId());
            }
						// The task is scheduled to be executed
            cancelScheduledTasks();
            // If APPINFO was registered
            if(applicationInfoManager ! =null
                    && clientConfig.shouldRegisterWithEureka()
                    && clientConfig.shouldUnregisterOnShutdown()) {
              	// Set the service instance status to DOWN
                applicationInfoManager.setInstanceStatus(InstanceStatus.DOWN);
              	// Unregister
                unregister();
            }
						// Close the Jersey client
            if(eurekaTransport ! =null) {
                eurekaTransport.shutdown();
            }
            heartbeatStalenessMonitor.shutdown();
            registryStalenessMonitor.shutdown();
            logger.info("Completed shut down of DiscoveryClient"); }}Copy the code