Eureka Service Registry

1.1 About the Service Registry

Note: Service registries are essentially decoupled service providers and service consumers.

In principle, multiple providers should exist or be supported for any microservice (such as resume microservices deploying multiple instances) due to the distributed nature of the microservice.

Furthermore, in order to support elastic scaling characteristics, the number and distribution of providers of a microservice is often dynamic and cannot be determined in advance. As a result, the static LB mechanism commonly used in the singleton application phase is no longer suitable, and an additional component needs to be introduced to manage the registration and discovery of microservice providers, and this component is the service registry

1.1.1 General principles of a service registry

In distributed microservice architecture, the service registry is used to store the address information of service providers and the attribute information related to service publishing. Consumers can obtain the address information of service providers through active query and passive notification, instead of obtaining the address information of providers through hard coding. The consumer only needs to know which services are published in the current system, not exactly where the services exist. This is transparent routing.

  1. Service provider startup
  2. Service providers actively register relevant service information into the registry
  3. Service consumerTo obtainService Registration Information:
    • Pull pattern: Service consumers can actively pull the list of available service providers (existing Eureka approach)
    • Push mode: Service consumers subscribe to the service (When the service provider changes, the registry will actively push the updated service list to consumers (Eureka future plan mode, but later announced to stop, this is not then)
  4. The service consumer invokes the service provider directly

In addition, the registry also needs to complete the health monitoring of service providers, when it is found that the failure of service providers need to be removed in time;

1.1.2 Comparison of mainstream service centers

  • Zookeeper

    Zookeeper is a distributed service framework and a subproject of Apache Hadoop. It is used to solve data management problems frequently encountered in distributed applications, such as unified naming service, status synchronization service, cluster management, and configuration item management of distributed applications.

    To put it simply, the essence of ZooKeeper = storage + listening for notifications.

    Zookeeper is used as a service registry mainly because it has the node change notification function. As long as the client listens to related service nodes, all changes of service nodes can be timely notified to the listening client. In this way, as long as the caller uses the Zookeeper client, it can realize the subscription and change notification function of the service node, which is very convenient. In addition, Zookeeper availability is ok, because as long as more than half of the election nodes are alive, the entire cluster is available.

  • Eureka

    Open source by Netflix and integrated into the SpringCloud architecture by Pivatal, it is a service registration and discovery component developed based on RestfulAPI style

  • Consul

    Consul is a multi-data center distributed, highly available service publishing and registration software developed by HashiCorp based on the Go language. Consul uses Raft algorithm to ensure service consistency and supports health checks.

  • Nacos

    Nacos is a dynamic service discovery, configuration management, and service management platform that makes it easier to build cloud-native applications. Simply put, Nacos is the combination of registry and configuration center, which helps us solve the problems of service registration and discovery, service configuration and service management that will be involved in micro-service development. Nacos is one of the core components of Spring Cloud Alibaba and is responsible for service registration and discovery, as well as configuration. Nacos is mainly used in Ali Cloud. One application can cover two important scenarios, which reduces the deployment cost a lot

1.2 Service registry component Eureka

Eureka infrastructure

  1. Eureka Server registry is the Server side, we need to create our own project, import the Server side Jar, and then start

  2. ApplicationService is a Client service provider. As a Client micro service, you need to import the JAR related to the Eureka Client and configure it before establishing a connection with the Eureka Server

  3. Once the service provider is started, it registers the service with the registry

  4. After registering with the registry, the service provider needs to send a heartbeat to the registry every 30 seconds to renew

  5. The ApplicationClient client consumer, also acting as a client, periodically pulls the list of services and caches the list when pulled

  6. ApplicationClient selects a specific service from the list to access directly

Eureka interaction process and principle

Eureka consists of two components: Eureka Server and Eureka Client

Eureka Client is a Java Client that simplifies the interaction with Eureka Server. Eureka Server provides the capability of service discovery. When each micro-service is started, it registers its own information (such as network information) with the Eureka Server through the Eureka Client, and the Eureka Server stores the information of the service

  1. In the figure, US-EAST-1C, US-EAST-1D, and US-EAST-1E represent different areas, that is, different equipment rooms

  2. Each Eureka Server in the figure is a cluster

  3. In the figure, Application Service registers the Service with Eureka Server as the Service provider. After receiving the registration event, Eureka Server will perform data synchronization in the cluster and partition. As a consumer (service consumer), ApplicationClient can obtain service registration information from Eureka Server to make service calls. The service can also unregister or act as a client to obtain registry information

  4. Once the microservice (calling client, service provider client) is started, it periodically sends heartbeat (default interval is 30 seconds) to Eureka Server to renew its information

  5. If EurekaServer does not receive the heartbeat of a microservice node within a certain period of time, EurekaServer will log out of the microservice node (default: 90 seconds).

  6. Each Eureka Server is also a Eureka Client. Multiple Eureka servers synchronize service registration lists through replication

  7. The Eureka Client caches information in the Eureka Server. Even if all Eureka Server nodes go down, the service consumer can still find the service provider Eureka using the information in the cache. Improve the flexibility, scalability and availability of the system through heartbeat detection, health check and client caching

1.3 Eureka application and HA Cluster

The next step is to transform our resume micro-service step by step, which is completed in five steps

  1. Set up the singleton Eureka Server service registration center
  2. Set up a Eureka Server cluster
  3. Microservice provider — > Register with Eureka Server cluster
  4. Microservice consumers — > Register with the Eureka Server cluster
  5. Service consumer invokes service provider (via Eureka)

1.3.1 Setting up a Singleton Eureka Server Service Registry

The POM in lagou-parent introduces Spring Cloud dependencies in the parent project

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Greenwich.RELEASE</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
Copy the code

Build SpringBoot project based on Maven and build EurekaServer service (Lagou-Cloud-Eureka-server-8761) on SpringBoot project

The project structure is as follows

pom.xml

<? xml version="1.0" encoding="UTF-8"? > <project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>lagou-parent</artifactId>
        <groupId>com.lagou.edu</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0. 0</modelVersion>

    <artifactId>lagou-cloud-eureka-server-8761</artifactId> <dependencies> <! - Eureka server rely on - > < the dependency > < groupId > org. Springframework. Cloud < / groupId > <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId> </dependency> </dependencies> </project>Copy the code

LagouEurekaServerApp8761

@SpringBootApplication
// Declare that the current project is Eureka service
@EnableEurekaServer
public class LagouEurekaServerApp8761 {
    public static void main(String[] args) { SpringApplication.run(LagouEurekaServerApp8761.class,args); }}Copy the code

application.yml

server:
  port: 8761
spring:
  application:
    name: lagou-cloud-eureka-server The application name will be used as the service name in Eureka

The eureka Server is also a Client
eureka:
  instance:
    hostname: localhost Host name of the current Eureka instance

  client:
    service-url: The address of the Eureka Server that the client interacts with points to its own address
      defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka  #http://localhost:8761/eureka

    register-with-eureka: false  You do not need to register as a Server
    fetch-registry: false The Eureka Server does not need to obtain information from Eureka Server
 
Copy the code
  • The main function of execution start class LagouCloudEurekaServerApplication
  • Visit http://127.0.0.1:8761. If you see the following page (background of Eureka Registry), EurekaServer is successfully published

Sample code for a single Eureka instance

1.3.2 Setting up a Eureka Server HA Cluster

In Internet applications, service instances are rarely single.

Even if the microservice consumer caches the list of services, if EurekaServer has only one instance and the instance dies, the entire system will be affected by the fact that the service instance in the microservice consumer’s local cache list is also unavailable.

In a production environment, we will configure the Eureka Server cluster for high availability. Nodes in the Eureka Server cluster share the service registry through point-to-point (P2P) communication. We turned on two Eurekaservers to set up the cluster.

Specific steps

(1) Modify the local host attribute because it is difficult to simulate the situation of multiple hosts in the test on a personal computer, Eureka needs to execute the host address when configuring the server cluster. Therefore, you need to change the host address on the PC

127.0.0.1 LagouCloudEurekaServerA
127.0.0.1 LagouCloudEurekaServerB
Copy the code

(2) Copy the newly created single instance Lagou-Cloud-eureka-server-8761, rename it to Lagou-cloud-eureka-server-8762, change all the words of 8761 in the project to 8762

(3) Change the application. Yml files of two Eureka projects

The application of 8761. Yml

server:
  port: 8761
spring:
  application:
    name: lagou-cloud-eureka-server The application name will be used as the service name in Eureka

The eureka Server is also a Client
eureka:
  instance:
    hostname: LagouCloudEurekaServerA Host name of the current Eureka instance

  client:
    service-url: The address of the Eureka Server that the client interacts with points to its own address
      In cluster mode, use commas to add other Server instances if there are other Server instances
      defaultZone: http://LagouCloudEurekaServerB:8762/eureka  #http://localhost:8761/eureka

    register-with-eureka: true  In cluster mode, true
    fetch-registry: true In cluster mode, true
Copy the code

The application of 8762. Yml

server:
  port: 8762
spring:
  application:
    name: lagou-cloud-eureka-server The application name will be used as the service name in Eureka

The eureka Server is also a Client
eureka:
  instance:
    hostname: LagouCloudEurekaServerB Host name of the current Eureka instance

  client:
    service-url: The address of the Eureka Server that the client interacts with points to its own address
      In cluster mode, use commas to add other Server instances if there are other Server instances
      defaultZone: http://LagouCloudEurekaServerA:8761/eureka  #http://localhost:8761/eureka

    register-with-eureka: true  In cluster mode, true
    fetch-registry: true In cluster mode, true
Copy the code

Here’s what needs to be said:

  1. The hostname and eureka.instance.hostname values of the current Eureka service are LagouCloudEurekaServerA and LagouCloudEurekaServerB respectively
  2. In cluster mode, the value of eureka.client.service-url.defaultzone refers to other registry services. If there are other Server instances, use commas to add them

LagouCloudEurekaServerA page

LagouCloudEurekaServerB page

As you can see, the two services reference each other, thus completing the construction of the Eureka Server cluster

HA instance code

1.3.3 Microservice Providers – > Register with Eureka Server cluster

(1) Spring-Cloud-Commons dependency is introduced in the parent project

<dependency>
  <groupId>org.springframework.cloud</groupId>
  <artifactId>spring-cloud-commons</artifactId>
</dependency>
Copy the code

(2) We opened the resume micro-service project Lagou-service-resume, opened the POM file to introduce the coordinates, and added the relevant coordinates of Eureka client

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

(3) Add notes above the startup program

The @enableEuRekaclient annotation is unique to Eureka and has the same effect as EnableDiscoveryClient. The @enableDiscoveryClient annotation enables the registry client to be universal. Eureka and Nacos can also be used

Starting with the Edgware version of SpringCloud, it is OK to do so without annotations, but it is recommended

@SpringBootApplication
@EntityScan("com.lagou.edu.pojo") 
@EnableDiscoveryClient // Enable registry client generic
public class LagouResumeApplication {
    public static void main(String[] args) { SpringApplication.run(LagouResumeApplication.class, args); }}Copy the code

(4) Add the address and related configurations of Eureka Server HA cluster in application.yml

Basic annotations are added with eureka.client.service-url.defaultzone to configure the service address

# Register with Eureka Service Center
eureka:
  client:
    service-url:
      To register a cluster, connect multiple Eureka addresses using commas
      defaultZone: http://LagouCloudEurekaServerA:8761/eureka,http://LagouCloudEurekaServerB:8762/eureka
Copy the code

Eureka.instance. prefer-ip-address Indicates the IP address of the service instance instead of the host name. Eureka.instance. instance-id Indicates the name of the user-defined instance

# Register with Eureka Service Center
eureka:
  client:
    service-url:
      To register a cluster, connect multiple Eureka addresses using commas
      defaultZone: http://LagouCloudEurekaServerA:8761/eureka,http://LagouCloudEurekaServerB:8762/eureka
  instance:
    prefer-ip-address: true Display IP address in service instance instead of host name
    # IP - address: 192.168.56.1
    # custom instance name
    instance-id: ${spring.cloud.client.ip-address}:${spring.application.name}:${server.port}:@project.version@
Copy the code

Resume microservice registration to Eureka code example

1.3.4 Microservice Consumers — > Register with Eureka Server cluster

This time, the lagou-service-AutoDeliver service is also registered in Eureka, which is the same as the resume micro-service

Join the rely on

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

Open Eureka client annotations

@SpringBootApplication
@EnableDiscoveryClient // Enable registry client generic
public class AutodeliverApplicaton {
    public static void main(String[] args) {
        SpringApplication.run(AutodeliverApplicaton.class,args);
    }

    // Use Resttemple to make a remote call, injecting the object first
    @Bean
    public RestTemplate getRestTemplete(a){
        return newRestTemplate(); }}Copy the code

Update the application.yml configuration file

server:
  port: 8090
spring:
  application:
    name: lagou-service-autodeliver

eureka:
  client:
    service-url:
      To register a cluster, connect multiple Eureka addresses using commas
      defaultZone: http://LagouCloudEurekaServerA:8761/eureka,http://LagouCloudEurekaServerB:8762/eureka
  instance:
    prefer-ip-address: true Display IP address in service instance instead of host name
    # IP - address: 192.168.56.1
    # custom instance name
    instance-id: ${spring.cloud.client.ip-address}:${spring.application.name}:${server.port}:@project.version@
Copy the code

After enabling all four services, you can see that the resume delivery service is already online

Resume delivery microservice access Eureka instance code

1.3.5 Service Consumer invokes Service Provider (via Eureka)

Now let’s look at the Controller service for Lagou-service-AutoDeliver

Before the original way, we manually wrote the address of the resume micro service in the code, which would bring a lot of inconvenience, unable to do load balancing, unable to use the cluster

@Autowired
    private RestTemplate restTemplate;
 
    @GetMapping("/old/checkState/{userId}")
    public Integer findResumeOpenState_old(@PathVariable Long userId){
        // Invoke the remote service --> resume microservice

        // Get the result directly
        Integer forObject = restTemplate.getForObject("http://localhost:8080/resume/openstate/" + userId, Integer.class); 
        return forObject;
    }
Copy the code

How to use the service center

  1. Weaves the DiscoveryClient service to discover clients
  2. Get service instance information from Eureka Server
  3. If there are multiple instances, select one to use
  4. Get the host port from the metadata information
  5. Assemble the URL address and invoke it from RestTemplate
    @Autowired
    private DiscoveryClient discoveryClient;
 	
    @Autowired
    private RestTemplate restTemplate;
    
    @GetMapping("/checkState/{userId}")
    public Integer findResumeOpenState(@PathVariable Long userId){

        Obtain service instance information from Eureka Server
        List<ServiceInstance> instances = discoveryClient.getInstances("lagou-service-resume");

        //2. If there are multiple instances, select one to use
        ServiceInstance serviceInstance = instances.get(0);

        //3. Obtain the host port from the metadata
        String host = serviceInstance.getHost();
        int port = serviceInstance.getPort();
        String url="http://"+host+":"+port+"/resume/openstate/" + userId;

        System.out.println("Eureka Service information >>>>>>>>>>>>>>>"+url);

        Integer forObject = restTemplate.getForObject(url ,Integer.class);

        return forObject;
    }
Copy the code

Summary: Although it is a little more advanced than the previous way, it is still more complicated. The better way will be introduced later, and then the transformation will be carried out

Service invocation code example

1.4 Details of Eureka

1.4.1 Eureka Metadata Description

Eureka metadata comes in two types: standard metadata and custom metadata.

Standard metadata: host names, IP addresses, port numbers, and other information are published in the service registry for calls between services.

User-defined metadata: You can use eureka.instance.metadata-map to customize metadata in the KEY/VALUE format. This metadata can be accessed from a remote client.

Yml file in lagou-service-resume. Add eureka.instance.metadata-map at the bottom and key-value pairs at the bottom

instance:
 prefer-ip-address: true
 metadata-map:
 # Custom metadata (kv custom)
 cluster: cl1
 region: shanghai
Copy the code

Let’s test the custom data we just configured in the resume microservice in lagou-service-AutoDeliver. Here we create a new test class

It is emphasized here

  • @ SpringBootTest (classes = {AutodeliverApplicaton. Class}) set to start SpringBoot program entrance
  • @ RunWith (SpringJUnit4ClassRunner. Class) to replace the Junit runner by default
import java.util.List;
// Set the SpringBoot boot entry
@SpringBootTest(classes = {AutodeliverApplicaton.class})

// Replace the default Junit runner
@RunWith(SpringJUnit4ClassRunner.class)
public class AutoDeliverApplicationTest {

    @Autowired
    private DiscoveryClient discoveryClient;

    @Autowired
    private RestTemplate restTemplate;

    @Test
    public void testInstanceMetaData(a){
        List<ServiceInstance> instances = discoveryClient.getInstances("lagou-service-resume");
        for (int i = 0; i < instances.size(); i++) { ServiceInstance serviceInstance = instances.get(i); System.out.println(serviceInstance); }}}Copy the code

Standard metadata – graphical representation

Custom metadata – icon

Example of using metadata

1.4.2 Eureka Client detailed solution

The service provider (also the Eureka client) registers the service with EurekaServer and completes the service renewal

Service Registration Details (Service Provider)

(1) When we import the eureka-client dependent coordinates, configure the Address of the Eureka service registry

(2) When the service is started, it will initiate registration request to the registry, carrying service metadata information

(3) The Eureka registry will store the service information in the Map.

Service Renewal Details (Service Provider)

Services renew (heartbeat) to the registry every 30 seconds (also known as registration). If they are not renewed, the lease expires after 90 seconds, and then the service is invalidated. Renewal every 30 seconds is called heartbeat detection

Register service with Eureka service center cluster
eureka:
 instance:
 Lease renewal interval, default 30 seconds
 lease-renewal-interval-in-seconds: 30
 EurekaServer will remove the service from the list if there is no heartbeat after 90 seconds
 lease-expiration-duration-in-seconds: 90
Copy the code

Access to the list of services (Service Consumers)

The service pulls a list of services from the registry every 30 seconds, which can be modified by configuration. Often we don’t need to adjust

Register service with Eureka service center cluster
eureka:
 client:
 # How often to pull the service list
 registry-fetch-interval-seconds: 30
Copy the code

(1) When the service consumer starts, it will obtain the read-only backup from the EurekaServer service list and cache it locally

(2) Data will be retrieved and updated every 30 seconds

(3) The interval of 30 seconds can be changed by configuring eureka.client.registry-fetch-interval-seconds

1.4.3 Eureka Service Description

The following describes several states of the server

Service offline

1) When the service is shut down normally, the REST request of the service offline will be sent to EurekaServer

2) After receiving the request, the service center puts the service offline

Failure to eliminate

Eureka Server will be timed (Eureka.server. Eviction – interval-timer-in-MS, default 60 seconds) for inspection. If the instance does not receive heartbeat within a certain period of time (defined by eureka.instance.lease-expiration-duration-in-seconds, which is 90 seconds by default), the instance is deregister

To protect themselves

Service provider – > Registry

Periodic renewal (service provider and registry communication), if there is a problem with the network between the service provider and the registry, does not mean that the service provider is unavailable, does not mean that the service consumer cannot access the service provider, network partitioning occurs

If more than 85% of the client nodes have no normal heartbeat within 15 minutes, Eureka considers that there is a network failure between the client and the registry, and The Eureka Server automatically goes into self-protection.

Why is there a self-protection mechanism?

By default, If Eureka Server does not receive a heartbeat from a microservice instance within a certain period of time (90 seconds by default), Eureka Server will remove the instance. However, when the network partition failure occurs, the microservice cannot communicate with Eureka Server normally, and the microservice itself is running normally. At this time, the microservice should not be removed, so self-protection mechanism is introduced

The following information is displayed on the service center page

When you’re in self-preservation mode

1) No service instances will be removed (possibly due to network problems between service providers and EurekaServer), ensuring that most services are still available

2) The Eureka Server can still accept registration and query requests for new services, but will not be synchronized to other nodes to ensure that the current node is still available. When the network is stable, the new registration information of the current Eureka Server will be synchronized to other nodes.

3) In the Eureka Server project, eureka.server.enable-self-preservation can be configured to disable self-preservation. The default value is on

eureka:
 server:
 enable-self-preservation: false # Turn off self-protection mode (default: turn on)
Copy the code

1.5 Eureka core source code analysis

1.5.1 Eureka Server Startup Process

SpringCloud, take full advantage of the characteristics of the automatic assembly of SpringBoot SpringBoot application when they start loading EurekaServerAutoConfiguration automatic configuration classes

The spring.factories configuration file is stored under meta-INF

The following is the key to see the EurekaServerAutoConfiguration class have what information

EurekaServerAutoConfigurationClass for that

# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # org.springframework.cloud.netflix.eureka.server.EurekaServerAutoConfiguration # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #@Configuration
@Import(EurekaServerInitializerConfiguration.class)
@ConditionalOnBean(EurekaServerMarkerConfiguration.Marker.class)
@EnableConfigurationProperties({ EurekaDashboardProperties.class,InstanceRegistryProperties.class })
@PropertySource("classpath:/eureka/server.properties")
public class EurekaServerAutoConfiguration extends WebMvcConfigurerAdapter {... }Copy the code

By observing the class head, we can preliminarily get the following information:

  • @ConditionalOnBean(EurekaServerMarkerConfiguration.Marker.class)
    • When we assemble this class, we must require the presence of the containerMarkerClass, so where does this class come from?
  • EurekaServerAutoConfiguration this is the main class, need to focus on
  • Import EurekaServerInitializerConfiguration such a configuration class

The timing of Marker class injection is actually the annotation that marks the current item as EurekaServer on the launcher class

EurekaServerAutoConfigurationClass correlation resolution

  • eurekaController

Register the external dashboard interface, which is the default startup page of Eureka. You can disable the display by running eureka.dashboard.enable=false

# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # org.springframework.cloud.netflix.eureka.server.EurekaServerAutoConfiguration#eurekaController # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #@Bean
  @ConditionalOnProperty(prefix = "eureka.dashboard", name = "enabled", matchIfMissing = true)
  public EurekaController eurekaController(a) {
      return new EurekaController(this.applicationInfoManager);
  }
  static {
      CodecWrappers.registerWrapper(JACKSON_JSON);
      EurekaJacksonCodec.setInstance(JACKSON_JSON.getCodec());
  }
Copy the code
  • PeerAwareInstanceRegistry 【 methods 】

Peer nodes are aware of instance registries (registries used by registration services in cluster mode). All nodes in a EurekaServer cluster are peers without primary and secondary

# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # org.springframework.cloud.netflix.eureka.server.EurekaServerAutoConfiguration#peerAwareInstanceRegistry # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #@Bean
public PeerAwareInstanceRegistry peerAwareInstanceRegistry( ServerCodecs serverCodecs) {
    this.eurekaClient.getApplications(); // force initialization
    return new InstanceRegistry(this.eurekaServerConfig, this.eurekaClientConfig,
            serverCodecs, this.eurekaClient,
            this.instanceRegistryProperties.getExpectedNumberOfClientsSendingRenews(),
            this.instanceRegistryProperties.getDefaultOpenForTrafficCount());
}
Copy the code
  • peerEurekaNodes

PeerEurekaNodes is injected to help encapsulate peer node-related information and operations, such as updating peer machines in the cluster

# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # org.springframework.cloud.netflix.eureka.server.EurekaServerAutoConfiguration#peerEurekaNodes # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #@Bean
@ConditionalOnMissingBean
public PeerEurekaNodes peerEurekaNodes(PeerAwareInstanceRegistry registry, ServerCodecs serverCodecs) {
    return new RefreshablePeerEurekaNodes(registry, this.eurekaServerConfig,
            this.eurekaClientConfig, serverCodecs, this.applicationInfoManager);
}
Copy the code

We went into the PeerEurekaNodes method, looked inside, and found that there was a start method

1- A thread pool is built

2- Thread pool updates peer node information

# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # com.netflix.eureka.cluster.PeerEurekaNodes#start # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #public void start(a) {
 taskExecutor = Executors.newSingleThreadScheduledExecutor();
 try {
     updatePeerEurekaNodes(resolvePeerUrls());
     Runnable peersUpdateTask = new Runnable() {
         @Override
         public void run(a) {
             try{ updatePeerEurekaNodes(resolvePeerUrls()); }}Copy the code

So when exactly is this update method executed?

  • eurekaServerContext

Master configuration in the class eurekaServerContext method, need to inject a PeerEurekaNodes, then assigned to DefaultEurekaServerContext class

# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # org.springframework.cloud.netflix.eureka.server.EurekaServerAutoConfiguration#eurekaServerContext # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #@Bean
public EurekaServerContext eurekaServerContext(ServerCodecs serverCodecs, PeerAwareInstanceRegistry registry, PeerEurekaNodes peerEurekaNodes) {
    return new DefaultEurekaServerContext(this.eurekaServerConfig, serverCodecs,
            registry, peerEurekaNodes, this.applicationInfoManager);
}
Copy the code

We check the DefaultEurekaServerContext class, found @ PostConstruct annotations, which represents the class once initialized, executes the initialize method

# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # com.netflix.eureka.DefaultEurekaServerContext#DefaultEurekaServerContext # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #@Inject
public DefaultEurekaServerContext(... }@PostConstruct
@Override
public void initialize() {
    logger.info("Initializing ...");
    peerEurekaNodes.start();
    try {
        registry.init(peerEurekaNodes);
    } catch (Exception e) {
        throw new RuntimeException(e);
    }
    logger.info("Initialized");
}
Copy the code
  • eurekaServerBootstrap

Inject the EurekaServerBootstrap class, which will be used for subsequent startup. In the framework, the method of injecting beans is used to inject related components one by one, making the whole architecture more clear

# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # org.springframework.cloud.netflix.eureka.server.EurekaServerAutoConfiguration#eurekaServerBootstrap # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #@Bean
public EurekaServerBootstrap eurekaServerBootstrap(PeerAwareInstanceRegistry registry,EurekaServerContext serverContext) {
    return new EurekaServerBootstrap(this.applicationInfoManager,this.eurekaClientConfig, this.eurekaServerConfig, registry,serverContext);
}
Copy the code
  • jerseyFilterRegistration

Register Jersey filter. Jersey is a REST framework that helps us publish restful service interfaces. Similar to SpringMVC, the framework is very lightweight and easy to use for restful functions

# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # org.springframework.cloud.netflix.eureka.server.EurekaServerAutoConfiguration#jerseyFilterRegistration # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #@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;
}
Copy the code

Relevant analytic EurekaServerInitializerConfiguration class

We found that this class implements the SmartLifecycle interface, which can do something after the Beans in the Spring container have been created (the Start method)

# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # org.springframework.cloud.netflix.eureka.server.EurekaServerInitializerConfiguration # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #@Configuration
public class EurekaServerInitializerConfiguration
		implements ServletContextAware.SmartLifecycle.Ordered {}Copy the code

Within the start method, the contextInitialized method of eurekaServerBootstrap is executed

# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # org.springframework.cloud.netflix.eureka.server.EurekaServerInitializerConfiguration#start # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #@Override
public void start(a) {
    new Thread(new Runnable() {
        @Override
        public void run(a) {
            try {
                eurekaServerBootstrap.contextInitialized(EurekaServerInitializerConfiguration.this.servletContext);
                publish(new EurekaRegistryAvailableEvent(getEurekaServerConfig()));
                EurekaServerInitializerConfiguration.this.running = true;
                publish(new EurekaServerStartedEvent(getEurekaServerConfig()));
            }
            catch (Exception ex) {
                // Help!
                log.error("Could not initialize Eureka servlet context", ex);
            }
        }
    }).start();
}
Copy the code

Focus on the contextInitialized method

# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # org.springframework.cloud.netflix.eureka.server.EurekaServerBootstrap#contextInitialized # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #public void contextInitialized(ServletContext context) {
  initEurekaEnvironment(); // Initialize the environment
  initEurekaServerContext(); // Initialize the Context details
  context.setAttribute(EurekaServerContext.class.getName(), this.serverContext);
} 
Copy the code

==> Focus on details

# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # org.springframework.cloud.netflix.eureka.server.EurekaServerBootstrap#initEurekaServerContext # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #protected void initEurekaServerContext(a) throws Exception {
		// For backward compatibility
        // Register the data type converter
		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();
		}

		// Provide an interface for non-IOC containers to get ServerContext objects
		EurekaServerContextHolder.initialize(this.serverContext);

		log.info("Initialized server context");

		// When a server instance is started, the registration information is copied (synchronized) from other servers in the cluster. Each server is also a client to other servers
		// Copy registry from neighboring eureka node
		int registryCount = this.registry.syncUp();
		this.registry.openForTraffic(this.applicationInfoManager, registryCount);

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

Explore this.registry. SyncUp (); What did you do

# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # com.netflix.eureka.registry.PeerAwareInstanceRegistryImpl#syncUp # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #public int syncUp(a) {
        // Copy entire entry from neighboring DS node
        int count = 0;
		
        // Try again because the remote server may not be connected once
        // So we write our own code, any call to the third party interface, remote call, better do a retry
        for (int i = 0; ((i < serverConfig.getRegistrySyncRetries()) && (count == 0)); i++) {
            if (i > 0) {
                try {
                	// Try again later
                    Thread.sleep(serverConfig.getRegistrySyncRetryWaitMs());
                } catch (InterruptedException e) {
                    logger.warn("Interrupted during registry transfer..");
                    break; }}// Obtain the registry information of other servers
            Applications apps = eurekaClient.getApplications();
            for (Application app : apps.getRegisteredApplications()) {
                for (InstanceInfo instance : app.getInstances()) {
                    try {
                        if (isRegisterable(instance)) {
                        	
                            // Register the remote information in your own registry
                            register(instance, instance.getLeaseInfo().getDurationInSecs(), true); count++; }}catch (Throwable t) {
                        logger.error("During DS init copy", t); }}}}return count;
    }
Copy the code

What does the register method do

# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # com.netflix.eureka.registry.AbstractInstanceRegistry#register # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #// The class maintains the Registry field, which is a registry
private final ConcurrentHashMap<String, Map<String, Lease<InstanceInfo>>> registry
            = new ConcurrentHashMap<String, Map<String, Lease<InstanceInfo>>>();

// Register the instance to the registry
public void register(InstanceInfo registrant, int leaseDuration, boolean isReplication) {
      try {
          read.lock();
          
          // Get the registry based on the application name
          Map<String, Lease<InstanceInfo>> gMap = registry.get(registrant.getAppName());
          REGISTER.increment(isReplication);
          if (gMap == null) {
              final ConcurrentHashMap<String, Lease<InstanceInfo>> gNewMap = new ConcurrentHashMap<String, Lease<InstanceInfo>>();
              
              // If no registry corresponding to the application name can be obtained, create an empty registry
              gMap = registry.putIfAbsent(registrant.getAppName(), gNewMap);
              if (gMap == null) { gMap = gNewMap; }}// Check whether the lease instance exists in the service. If not, create a lease object
          Lease<InstanceInfo> existingLease = gMap.get(registrant.getId());
          
Copy the code

Continue with openForTraffic

# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # com.netflix.eureka.registry.PeerAwareInstanceRegistryImpl#openForTraffic ####################################################################################### Override public void openForTraffic(ApplicationInfoManager applicationInfoManager, int count) { ...... logger.info("Changing status to UP"); / / instance state is set to the up applicationInfoManager. SetInstanceStatus (InstanceStatus. Up); Super.postinit (); // Enable scheduled tasks. By default, service invalidation occurs every 60 seconds. }Copy the code

Enter the postInit method to check

# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # com.netflix.eureka.registry.AbstractInstanceRegistry#postInit # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # AtomicReference is introduced1, AtomicReference and AtomicInteger are very similar, except that AtomicInteger encapsulates an integer, whereas AtomicReference corresponds to a plain object reference. That is, it ensures thread-safety when you modify object references. # #2AtomicReference is used to perform atomic operations on "objects". Provides an object reference variable that reads and writes atomically. Atoms mean that multiple threads trying to change the same AtomicReference(such as compare and swap operations) will not leave the AtomicReference in an inconsistent state. ## evictionTaskRef references a taskprivate final AtomicReference<EvictionTask> evictionTaskRef = new AtomicReference<EvictionTask>();

// Fail to cull tasks
protected void postInit(a) {
      renewsLastMin.start();
      if(evictionTaskRef.get() ! =null) {
          evictionTaskRef.get().cancel();
      }
      
      // Set EvictionTask==> Reject tasks
      evictionTaskRef.set(new EvictionTask()); 
      evictionTimer.schedule(evictionTaskRef.get(),
              serverConfig.getEvictionIntervalTimerInMs(),
              serverConfig.getEvictionIntervalTimerInMs());
  }
Copy the code

1.5.2 Eureka Server Service Interface Exposure Policy

The Jersey framework (a framework that publishes restful interfaces, similar to our SpringMVC) is registered with the main configuration class during Eureka Server startup.

# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # org.springframework.cloud.netflix.eureka.server.EurekaServerAutoConfiguration#jerseyFilterRegistration # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #// Jersey Filter registered at boot time
@Bean
public FilterRegistrationBean jerseyFilterRegistration(javax.ws.rs.core.Application eurekaJerseyApp) {
    FilterRegistrationBean bean = new FilterRegistrationBean();
    bean.setFilter(newServletContainer(eurekaJerseyApp)); .return bean;
}
Copy the code

Here is the Jersey detail configuration

==> This defines the path of the scanned package ==> So how to see the contents of the package? We'll just have to go bag by bagprivate static final String[] EUREKA_PACKAGES = new String[] { "com.netflix.discovery"."com.netflix.eureka" };
            
@Bean
public javax.ws.rs.core.Application jerseyApplication(Environment environment,ResourceLoader resourceLoader) {===> Here is the Jersey annotation for configuration, Path-> similar to that in springMVC@RequestMappingAnnotation provider. AddIncludeFilter (new AnnotationTypeFilter(Path.class));
    provider.addIncludeFilter(new AnnotationTypeFilter(Provider.class));

    // Find classes in Eureka packages (or subpackages)===> Scan annotations --> Specify the package to scan, which scans the package and its subclasses, doing something similar to spring Companent-scan Set<Class<? >> classes =new HashSet<>();
    for (String basePackage : EUREKA_PACKAGES) {
        Set<BeanDefinition> beans = provider.findCandidateComponents(basePackage);
        for(BeanDefinition bd : beans) { Class<? > cls = ClassUtils.resolveClassName(bd.getBeanClassName(), resourceLoader.getClassLoader()); classes.add(cls); }}}Copy the code

The package that Jersey scans, controller in springMvc, the class that jersey provides the interface to is called a resource

==> This defines the path of the scanned package ==> So how to see the contents of the package? We'll just have to go bag by bagprivate static final String[] EUREKA_PACKAGES = new String[] { "com.netflix.discovery"."com.netflix.eureka" };
Copy the code

In ApplicationResource class, provide the interface of the service, called resource in Jersey = = > com.net flix. Eureka. Resources. ApplicationResource

When we use SpringMvc, we define controller by controller, which is the concept of controller, and Jersey, which is the concept of resource

Resource class that provides services externally === => SpringMVC Controller

These are Restful service interfaces for Eureka Client using Jersey (complete service registration, heartbeat renewal, etc.)

1.5.3 Eureka Server Service Registration Interface (Accepting Client Registration)

ApplicationResource class addInstance() : registry.register(info,”true”.equals(isReplication));

# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # com.netflix.eureka.resources.ApplicationResource#addInstance # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #@POST
@Consumes({"application/json", "application/xml"})
public Response addInstance(InstanceInfo info,
                            @HeaderParam(PeerEurekaNode.HEADER_REPLICATION) String isReplication) {
    logger.debug("Registering instance {} (replication={})", info.getId(), isReplication);
    // validate that the instanceinfo contains all the necessary required fieldsWhen registering a service, you must verify the required information. If any configuration information is not met, you can return it directly400
    if (isBlank(info.getId())) {
        return Response.status(400).entity("Missing instanceId").build();
    } else if (isBlank(info.getHostName())) {
        return Response.status(400).entity("Missing hostname").build();
    } else if (isBlank(info.getIPAddr())) {
        return Response.status(400).entity("Missing ip address").build();
    } else if(isBlank(info.getAppName())) { ... } #===> Complete registry.register(info,"true".equals(isReplication)); #===> Registration success return code204
    return Response.status(204).build();  // 204 to be backwards compatible
}
Copy the code

Register service information and synchronize it to other Eureka nodes

# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # com.netflix.eureka.registry.PeerAwareInstanceRegistryImpl#register # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #@Override
public void register(final InstanceInfo info, final boolean isReplication) {## retrieve the default service aging configurationintleaseDuration = Lease.DEFAULT_DURATION_IN_SECS; If the client is actively configured with aging information, then the client configuration is takenif(info.getLeaseInfo() ! =null && info.getLeaseInfo().getDurationInSecs() > 0) { leaseDuration = info.getLeaseInfo().getDurationInSecs(); } ## call parent register to register the instancesuper.register(info, leaseDuration, isReplication); ReplicateToPeers (action.register, info.getAppName(), info.getid (), info,null, isReplication);
}
Copy the code

Replicate to the Eureka peer replicateToPeers

# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # com.netflix.eureka.registry.PeerAwareInstanceRegistryImpl#replicateToPeers # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #private void replicateToPeers(Action action, String appName, String id,
                              InstanceInfo info /* optional */,
                              InstanceStatus newStatus /* optional */.boolean isReplication) {
      
  for (final PeerEurekaNode node : peerEurekaNodes.getPeerEurekaNodes()) {
      // If the url represents this host, do not replicate to yourself.
      if (peerEurekaNodes.isThisMyUrl(node.getServiceUrl())) {
          continue; } # # peer node instance movement synchronization replicateInstanceActionsToPeers (action, appName, id, info, newStatus, node); }}Copy the code

Peer node instance replicateInstanceActionsToPeers movement synchronization

# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # com.netflix.eureka.registry.PeerAwareInstanceRegistryImpl#replicateInstanceActionsToPeers # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #private void replicateInstanceActionsToPeers(Action action, String appName, String id, InstanceInfo info, InstanceStatus newStatus, PeerEurekaNode node) { 
        InstanceInfo infoFromRegistry = null;
        CurrentRequestVersion.set(Version.V2);
        switch (action) {
            caseCancel: ## Cancel node. Cancel (appName, id);break;
            caseHeartbeat: ##case## Register...case StatusUpdate:
                ...
            caseDeleteStatusOverride: ... }}}Copy the code

1.5.4 Eureka Server Service Renewal Interface (Accepting Client Renewal)

There are interfaces for renewal, which are within the scope of instance resources, so we look at InstanceResource

The renewLease method completes client heartbeat processing at InstanceResource.

registry.renew(app.getName(), id, isFromReplicaNode);

@PUT
public Response renewLease(
        @HeaderParam(PeerEurekaNode.HEADER_REPLICATION) String isReplication,
        @QueryParam("overriddenstatus") String overriddenStatus,
        @QueryParam("status") String status,
        @QueryParam("lastDirtyTimestamp") String lastDirtyTimestamp) {
    boolean isFromReplicaNode = "true".equals(isReplication); ## Renew callbooleanisSuccess = registry.renew(app.getName(), id, isFromReplicaNode); . }Copy the code

The contract method

# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # com.netflix.eureka.registry.PeerAwareInstanceRegistryImpl#renew # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #public boolean renew(final String appName, final String id, final boolean isReplication) {## renew locallyif (super. Renew (appName, ID, isReplication)) {## renew is synchronized to another peer.Heartbeat
        replicateToPeers(Action.Heartbeat, appName, id, null.null, isReplication);
        return true;
    }
    return false;
}
Copy the code

The renew() method — > leasetorenew.renew () — > the final action to be performed is to update the last update timestamp

# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # com.netflix.eureka.lease.Lease#renew # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #public void renew(a) LastUpdateTimestamp = system.currentTimemillis () + duration; }Copy the code

1.5.5 Eureka Client Registration Service

Startup process: The Eureka client also loads a lot of configuration classes when it starts up. You can see the loaded configuration classes in the spring-cloud-Netflix-Eureka-client-2.1.0.release.jar file

Introducing the jar can be automatically assembled, analysis EurekaClientAutoConfiguration class first

# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # org.springframework.cloud.netflix.eureka.EurekaClientAutoConfiguration # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # = = > system to load the, Marker objects must be required in the container@ConditionalOnBean(EurekaDiscoveryClientConfiguration.Marker.class)If you do not want to act as a client, you can set eureka.client.enabled=false## At some point, we introduce this project@ConditionalOnProperty(value = "eureka.client.enabled", matchIfMissing = true)AutoConfigureAfter must assemble the configured bean before assembling the current class@AutoConfigureAfter(name = {"org.springframework.cloud.autoconfigure.RefreshAutoConfiguration", "org.springframework.cloud.netflix.eureka.EurekaDiscoveryClientConfiguration", "org.springframework.cloud.client.serviceregistry.AutoServiceRegistrationAutoConfiguration"})
public class EurekaClientAutoConfiguration {}
Copy the code

@ AutoConfigureAfter annotations, contain EurekaDiscoveryClientConfiguration, so we look at the contents of this class

# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # org.springframework.cloud.netflix.eureka.EurekaDiscoveryClientConfiguration # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #@Configuration
@EnableConfigurationProperties
@ConditionalOnClass(EurekaClientConfig.class)
@ConditionalOnProperty(value = "eureka.client.enabled", matchIfMissing = true)
public class EurekaDiscoveryClientConfiguration {

	class Marker {} ## ==> As soon as the current class is loaded into the container, it will automatically add a Marker to the container, so we don't need to add it@EnableDiscoveryClientThere's no problem@Bean
	public Marker eurekaDiscoverClientMarker(a) {
		return newMarker(); }}Copy the code

Thinking: What should EurekaClient do to start the process? We can think about it first and clarify our thoughts

1) Read the configuration file

2) Obtain service instance information from EurekaServer during startup

3) Register yourself to EurekaServer (addInstance)

4) Start some scheduled tasks (renew heartbeat, refresh local service cache list)

1) Read the configuration file

Read the encapsulation configuration information into the container

# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # org.springframework.cloud.netflix.eureka.EurekaClientAutoConfiguration#eurekaInstanceConfigBean # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #@Bean
@ConditionalOnMissingBean(value = EurekaInstanceConfig.class, search = SearchStrategy.CURRENT)
public EurekaInstanceConfigBean eurekaInstanceConfigBean(InetUtils inetUtils, ManagementMetadataProvider managementMetadataProvider) String HostName = getProperty("eureka.instance.hostname"); Read whether the IP address is displayed firstboolean preferIpAddress = Boolean.parseBoolean(getProperty("eureka.instance.prefer-ip-address")); String ipAddress = getProperty(String ipAddress = getProperty("eureka.instance.ip-address"); ## Service portint serverPort = Integer.valueOf(env.getProperty("server.port", env.getProperty("port"."8080"))); . EurekaInstanceConfigBean instance =newEurekaInstanceConfigBean(inetUtils); # # to assignment configuration object instance. SetNonSecurePort (serverPort); instance.setInstanceId(getDefaultInstanceId(env)); . ## Return ConfigBean objectreturn instance;
}
Copy the code

2) Obtain service instance information from EurekaServer during startup

Found, according to the source code in the class there are a lot of inner classes, we focus on EurekaClientConfiguration this class, you feeling should be on the name of it is more important. The eurekaClient method returns a client object. How is this client built?

# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # org.springframework.cloud.netflix.eureka.EurekaClientAutoConfiguration.EurekaClientConfiguration#eurekaClient # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #@Configuration
@ConditionalOnMissingRefreshScope
protected static class EurekaClientConfiguration {

    @Bean(destroyMethod = "shutdown")
    @ConditionalOnMissingBean(value = EurekaClient.class, search = SearchStrategy.CURRENT)
    public EurekaClient eurekaClient(ApplicationInfoManager manager, EurekaClientConfig config) {
        return new CloudEurekaClient(manager, config, this.optionalArgs,  this.context); }}Copy the code

With that in mind, let’s take a look at CloudEurekaClient’s constructor

# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # org.springframework.cloud.netflix.eureka.CloudEurekaClient # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #public class CloudEurekaClient extends DiscoveryClient { 
	public CloudEurekaClient(ApplicationInfoManager applicationInfoManager, EurekaClientConfig config, AbstractDiscoveryClientOptionalArgs
        args, ApplicationEventPublisher publisher) {
		super(applicationInfoManager, config, args); . }}Copy the code

Trace the source code, find its parent constructor, and finally locate the constructor. In this method, there is a lot of code, it looks dazzling, but we guess that we should be looking for information about the service, so we find the fetch word

# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # com.netflix.discovery.DiscoveryClient#DiscoveryClient # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #@InjectDiscoveryClient(ApplicationInfoManager applicationInfoManager, EurekaClientConfig config, AbstractDiscoveryClientOptionalArgs args,Provider<BackupRegistry> backupRegistryProvider) { ...... Get the list of information from the registryif(clientConfig.shouldFetchRegistry() && ! fetchRegistry(false)) { fetchRegistryFromBackup(); }... Register (); ## schedule task initScheduledTasks(); }Copy the code

Let’s focus on the fetchRegistry method, which reads the service information at startup and then fetches it in full or incrementally, depending on the condition

# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # com.netflix.discovery.DiscoveryClient#fetchRegistry # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #private boolean fetchRegistry(boolean forceFullRegistryFetch) Applications = getApplications();if(clientConfig.shouldDisableDelta() || (! Strings.isNullOrEmpty(clientConfig.getRegistryRefreshSingleVipAddress())) || forceFullRegistryFetch || (applications ==null)
          || (applications.getRegisteredApplications().size() == 0)
          || (applications.getVersion() == -1)) //Client application does not have latest library supporting deltaGetAndStoreFullRegistry (); }else{## increment getAndUpdateDelta(Applications); }return true;
}
Copy the code

3) Register yourself to EurekaServer

As we continue along the same lines, we find the words RegisterWithEureka and so on, and then we find a short and powerful word register. Generally speaking, the verb as the name of the method is usually more important

# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # com.netflix.discovery.DiscoveryClient#DiscoveryClient # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #@InjectDiscoveryClient(ApplicationInfoManager applicationInfoManager, EurekaClientConfig config, AbstractDiscoveryClientOptionalArgs args, Provider<BackupRegistry> backupRegistryProvider) { ...... ## ==> Whether to register in Eurekaif (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

Client registration method

# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # com.netflix.discovery.DiscoveryClient#register # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #boolean register(a) throws Throwable {
  logger.info(PREFIX + "{}: registering service...", appPathIdentifier);
  EurekaHttpResponse<Void> httpResponse;
  try{## send restful requests to server instances configured with ServerUrl. Register yourself # # layer using the Jersey client requests for remote 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() == Status.NO_CONTENT.getStatusCode();
}
Copy the code

4) Start some scheduled tasks (renew heartbeat, refresh local service cache list)

# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # com.netflix.discovery.DiscoveryClient#DiscoveryClient # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #@Inject
DiscoveryClient(ApplicationInfoManager applicationInfoManager, EurekaClientConfig config, AbstractDiscoveryClientOptionalArgs args,Provider<BackupRegistry> backupRegistryProvider) {
  if(clientConfig.shouldFetchRegistry() && ! fetchRegistry(false)) { fetchRegistryFromBackup(); } register(); ## =====> Schedule task initScheduledTasks(); }Copy the code

Delve deeper into initScheduledTasks

# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # com.netflix.discovery.DiscoveryClient#initScheduledTasks # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # timer refresh local service cache serviceclass CacheRefreshThread implements Runnable {
  public void run(a) { refreshRegistry(); }} ## Heartbeat renew scheduled taskclass HeartbeatThread implements Runnable {
    public void run(a) {## renew the contractif(renew ()) {# heartbeat success, modify the corresponding timestamp lastSuccessfulHeartbeatTimestamp = System. CurrentTimeMillis (); }}}Copy the code

Let’s focus on the renewal interface

# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # com.netflix.discovery.DiscoveryClient#renew # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #boolean renew(a) {# # to renew the contract interface sends the request httpResponse. = eurekaTransport registrationClient. SendHeartBeat (instanceInfo. GetAppName (), instanceInfo.getId(), instanceInfo,null); ## cannot be found, which may indicate that the current instance has been deleted by EurekaServerif (httpResponse.getStatusCode() == Status.NOT_FOUND.getStatusCode()) {

        longtimestamp = instanceInfo.setIsDirtyWithTime(); ## Re-register yourselfboolean success = register();
        if (success) {
            instanceInfo.unsetIsDirty(timestamp);
        }
        return success;
    }
    return httpResponse.getStatusCode() == Status.OK.getStatusCode();
}
 
Copy the code

Take a look at the sendHeartBeat interface in Renew

# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # com.netflix.discovery.DiscoveryClient#renew # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #public EurekaHttpResponse<InstanceInfo> sendHeartBeat(String appName, String id, InstanceInfo info, InstanceStatus overriddenStatus) {## request URL address String urlPath ="apps/" + appName + '/' + id;
    ClientResponse response = null; WebResource WebResource = JerseyClient.getClient ().resource(serviceUrl).path(urlPath) .queryParam("status", info.getStatus().toString())
            .queryParam("lastDirtyTimestamp", info.getLastDirtyTimestamp().toString());
    if(overriddenStatus ! =null) {
        webResource = webResource.queryParam("overriddenstatus", overriddenStatus.name()); }}Copy the code

1.5.6 Removing the Eureka Client Service

When we do our analysis, we find that EurekaClient is tagged with another @bean (destroyMethod = “shutdown”), indicating that the shutdown method will be called automatically when the service goes offline

# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # org.springframework.cloud.netflix.eureka.EurekaClientAutoConfiguration.EurekaClientConfiguration#eurekaClient # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #@Bean(destroyMethod = "shutdown")
@ConditionalOnMissingBean(value = EurekaClient.class, search = SearchStrategy.CURRENT)
public EurekaClient eurekaClient(ApplicationInfoManager manager, EurekaClientConfig config) {
    return new CloudEurekaClient(manager, config, this.optionalArgs,this.context);
}

Copy the code

The service offline method, after simplification, is nothing more than to shut down the resource and send the server a deregistration method

# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # com.netflix.discovery.DiscoveryClient#shutdown # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #@PreDestroy
@Override
public synchronized void shutdown(a) { 

  applicationInfoManager.unregisterStatusChangeListener(statusChangeListener.getId());

  cancelScheduledTasks();

  unregister();

  eurekaTransport.shutdown();

  heartbeatStalenessMonitor.shutdown();
  registryStalenessMonitor.shutdown();
}

Copy the code

To unregister the server, you can see that jersey is still used to send the cancel request

# # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # com.netflix.discovery.DiscoveryClient#unregister # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # # #void unregister(a) {
  EurekaHttpResponse<Void> httpResponse = eurekaTransport.registrationClient.cancel(instanceInfo.getAppName(), instanceInfo.getId());
}
Copy the code