This is the sixth day of my participation in the August Text Challenge.More challenges in August

>>>> 😜😜😜 Github: 👉 github.com/black-ant CASE Backup: 👉 gitee.com/antblack/ca…

A. The preface

Purpose of the article

  • Sort out Eureka service registration process
  • Deep service registry source code

Corresponding Configuration Class

Eureka Client configuration is through EurekaClientAutoConfiguration and EurekaDiscoveryClientConfiguration for processing

C- EurekaDiscoveryClientConfiguration
public EurekaDiscoveryClient discoveryClient(EurekaClient client, EurekaClientConfig clientConfig) {
   return new EurekaDiscoveryClient(client, clientConfig);
}

C- EurekaClientAutoConfiguration
Copy the code

Registration of services

How does Eureka register a service? Eureka registers a service with @enableDiscoveryClient

Eureka’s service registers the primary line for Lifecycle to initiate Eureka service registry’s Lifecycle control

The core service registry class is EurekaServiceRegistry, which provides the following methods

C-eurekaserviceregistry M-register: registers applications m-deregister M-setStatus: updates the status m-getStatusCopy the code

2.1 Starting point of Eureka registration

// C- EurekaServiceRegistry
public void start(a) {
   // only set the port if the nonSecurePort or securePort is 0 and this.port ! = 0
   if (this.port.get() ! =0) {
      if (this.registration.getNonSecurePort() == 0) {
         this.registration.setNonSecurePort(this.port.get());
      }

      if (this.registration.getSecurePort() == 0 && this.registration.isSecure()) {
         this.registration.setSecurePort(this.port.get()); }}// Only if nonSecurePort is greater than 0 and because the containerPortInitializer below is not running
   if (!this.running.get() && this.registration.getNonSecurePort() > 0) {
      // 2.1.1 -> Initiate registration logic
      this.serviceRegistry.register(this.registration);
      // Emit an InstanceRegisteredEvent event
      this.context.publishEvent(new InstanceRegisteredEvent<>(this.this.registration.getInstanceConfig()));
      this.running.set(true); }}Copy the code

Add: entry to start

// The Start method is based on the Lifecycle interface, which controls the Lifecycle of the Bean and calls the corresponding hook event whenever an Application starts or stops
public interface Lifecycle {
    void start(a);
    void stop(a);
    boolean isRunning(a);
}
Copy the code

2.1.1 Registration service

Register as a starting point for registration

// C- EurekaServiceRegistry
public void register(EurekaRegistration reg) {
   maybeInitializeClient(reg);
    
   // Initiate registration logic by changing the status
   / / PS: deregister is set to InstanceInfo InstanceStatus. DOWN
   reg.getApplicationInfoManager()
         .setInstanceStatus(reg.getInstanceConfig().getInitialStatus());
    
   // Health check
   reg.getHealthCheckHandler().ifAvailable(healthCheckHandler -> reg
         .getEurekaClient().registerHealthCheck(healthCheckHandler));
}


Copy the code

Step 2: Modify the state and invoke the listener

C- EurekaServiceRegistry
public synchronized void setInstanceStatus(InstanceStatus status) {
    // InstanceStatus.UP
    InstanceStatus next = instanceStatusMapper.map(status);
    if (next == null) {
        return;
    }

    InstanceStatus prev = instanceInfo.setStatus(next);
    if(prev ! =null) {
        for (StatusChangeListener listener : listeners.values()) {
                // You can see that the state change event is sent by calling the listener -> Step 3
                listener.notify(newStatusChangeEvent(prev, next)); }}}// Add: StatusChangeListener object
public static interface StatusChangeListener {
    String getId(a);
    void notify(StatusChangeEvent statusChangeEvent);
}

// Add: StatusChangeEvent object
public class StatusChangeEvent extends DiscoveryEvent {
    // There are two states recorded in this object, the former and the present, but this seems to be just for printing the log, there is no difference between TODO
    private final InstanceInfo.InstanceStatus current;
    private final InstanceInfo.InstanceStatus previous;
}
Copy the code

Step 3: Initiate the update logic to update the remote server

C- InstanceInfoReplicator
public boolean onDemandUpdate(a) {
    if (rateLimiter.acquire(burstSize, allowedRatePerMinute)) {
            // Omit unnecessary concerns. You can see that a scheduled task is initiated via ScheduledExecutorService
            scheduler.submit(new Runnable() {
                @Override
                public void run(a) {
                    // private final AtomicReference<Future> scheduledPeriodicRef;
                    Future latestPeriodic = scheduledPeriodicRef.get();
                    if(latestPeriodic ! =null && !latestPeriodic.isDone()) {
                        latestPeriodic.cancel(false);
                    }
                    // Update local instance information and copy to remote server -> Step 4
                    InstanceInfoReplicator.this.run(); }}); }}Copy the code

Added: InstanceInfoReplicator’s role

  • A single update thread is configured to ensure continuous updates to the remote server
  • Update tasks can be scheduled on demand via onDemandUpdate()
  • The task processing rate is limited by the burst size
  • A new update task is always automatically scheduled after an earlier update task.
    • However, if the on-demand update task is started, the scheduled automatic update task is discarded (the new on-demand update task will schedule the new task after the new on-demand update task)

Call InstanceInfoReplicator # run

public void run(a) {
    try {
        // Refresh the current local InstanceInfo with the isdirty flag set to true
        discoveryClient.refreshInstanceInfo();

        Long dirtyTimestamp = instanceInfo.isDirtyWithTime();
        if(dirtyTimestamp ! =null) {
            // Initiate service registration
            discoveryClient.register();
            // If unsetDirtyTimestamp matches lastDirtyTimestamp, the dirty flag is cancelledinstanceInfo.unsetIsDirty(dirtyTimestamp); }}catch (Throwable t) {
    } finally {
        Future next = scheduler.schedule(this, replicationIntervalSeconds, TimeUnit.SECONDS); scheduledPeriodicRef.set(next); }}Copy the code

2.2 Registration Service

This is where the registration logic begins with DiscoveryClient:

C- DiscoveryClient
boolean register(a) throws Throwable {
    EurekaHttpResponse<Void> httpResponse;
    try {
        // Initiate registration logic
        httpResponse = eurekaTransport.registrationClient.register(instanceInfo);
    } catch (Exception e) {
        throw e;
    }
    // This return does not have much effect on the business
    return httpResponse.getStatusCode() == Status.NO_CONTENT.getStatusCode();
}
Copy the code

2.3 Register Loop Invocation

// This is very similar to the following pattern, which is to pass in an anonymous function and then initiate a callback from it
C- EurekaHttpClientDecorator
public EurekaHttpResponse<Void> register(final InstanceInfo info) {
    return execute(new RequestExecutor<Void>() {
        @Override
        public EurekaHttpResponse<Void> execute(EurekaHttpClient delegate) {
            // Chain calls are made through the callback function
            return delegate.register(info);
        }

        @Override
        public RequestType getRequestType(a) {
            returnRequestType.Register; }}); }Copy the code

Details: Polling calls

// there is a chain call, called separately

  • C – SessionedEurekaHttpClient: mandatory in a regular interval (a session) to reconnect, prevent the client will always adhere to a specific Eureka server instance
  • C – RetryableEurekaHttpClient: retry fail on subsequent server in the cluster’s request
  • C – RedirectingEurekaHttpClient: the Client will be redirect Server, and for the final resolution of the endpoint to perform the request
  • C – MetricsCollectingEurekaHttpClient: collect and statistical JerseyApplicationClient sends the request and response behavior information
// The next HttpClient is usually built internally via clientFactory, which is passed in via the constructor in EurekaHttpClients
TransportUtils.getOrSetAnotherClient(eurekaHttpClientRef, clientFactory.newClient());


// There are several concerns among these:
// Concern 1: atomic object classes
private final AtomicReference<EurekaHttpClient> eurekaHttpClientRef = new AtomicReference<>();

Copy the code

Added: The purpose of the call, why loop several HttpClients here?

Looking at these HttpClients, they look more like filters, except that their function is to initiate requests and their structure is similar to a Filter chain

About this piece can be ChanZhang look, here only focus on the last one, namely MetricsCollectingEurekaHttpClient

2.4 the core HttpClient

C- MetricsCollectingEurekaHttpClient
protected <R> EurekaHttpResponse<R> execute(RequestExecutor<R> requestExecutor) {
    EurekaHttpClientRequestMetrics requestMetrics = metricsByRequestType.get(requestExecutor.getRequestType());
    // Measures the time it takes to execute some code
    Stopwatch stopwatch = requestMetrics.latencyTimer.start();
    try {
        // The most common pattern in HttpClient is this anonymous class callback
        EurekaHttpResponse<R> httpResponse = requestExecutor.execute(delegate);
        requestMetrics.countersByStatus.get(mappedStatus(httpResponse)).increment();
        return httpResponse;
    } catch (Exception e) {
        requestMetrics.connectionErrors.increment();
        exceptionsMetric.count(e);
        throw e;
    } finally {
        // The timer endsstopwatch.stop(); }}Copy the code

Added: creation of JerseyApplicationClient

/ / Step 1: create JerseyEurekaHttpClientFactory
public TransportClientFactory newTransportClientFactory(EurekaClientConfig clientConfig, Collection
       
         additionalFilters, InstanceInfo myInstanceInfo, Optional
        
          sslContext, Optional
         
           hostnameVerifier)
         
        
        {
    final TransportClientFactory jerseyFactory = JerseyEurekaHttpClientFactory.create(
            clientConfig,
            additionalFilters,
            myInstanceInfo,
            new EurekaClientIdentity(myInstanceInfo.getIPAddr()),
            sslContext,
            hostnameVerifier
    );
    
    // Create by factory
    final TransportClientFactory metricsFactory = MetricsCollectingEurekaHttpClient.createFactory(jerseyFactory);

    return new TransportClientFactory() {
        @Override
        public EurekaHttpClient newClient(EurekaEndpoint serviceUrl) {
            // Anonymous object callback function
            returnmetricsFactory.newClient(serviceUrl); }}; }// Step 2: constructor injection
private MetricsCollectingEurekaHttpClient(EurekaHttpClient delegate,
                                          Map<RequestType, EurekaHttpClientRequestMetrics> metricsByRequestType,
                                          ExceptionsMetric exceptionsMetric,
                                          boolean shutdownMetrics) {...Copy the code

2.5 Process of Sending a Server Request

// C- AbstractJerseyEurekaHttpClient
public EurekaHttpResponse<Void> register(InstanceInfo info) {
    // http://localhost:8088/eureka/
    String urlPath = "apps/" + info.getAppName();
    ClientResponse response = null;
    try {
        // Prepare the Build to construct the Request
        // serviceUrl -> http://localhost:8088/eureka/
        Builder resourceBuilder = jerseyClient.resource(serviceUrl).path(urlPath).getRequestBuilder();
        addExtraHeaders(resourceBuilder);
            
        // Make a request, using jersey
        response = resourceBuilder
                .header("Accept-Encoding"."gzip")
                .type(MediaType.APPLICATION_JSON_TYPE)
                .accept(MediaType.APPLICATION_JSON)
                .post(ClientResponse.class, info);
        return anEurekaHttpResponse(response.getStatus()).headers(headersOf(response)).build();
    } finally {
        if(response ! =null) { response.close(); }}}Copy the code

The Server side ApplicationResource will be called, as we’ll see later in the process

Added three.

3.1 Structure design of HttpClient

The HttpClient invocation pattern is a typical.. Design patterns, which make heavy use of features like AtomicReference primitives,

3.2 Why are atomic classes so widely used in Eureka?

First, atomic classes guarantee atomic operations mainly through CAS (compare and swap) + volatile and native methods

The ScheduledExecutorService is used to initiate the registration.

conclusion

Before looking at relatively new frameworks, you can often see a lot of Spring shadow, many will choose to use Spring as the management.

However, when I looked at Eureka’s framework, I realized that many frameworks are not integrated with Spring. Spring is very useful, but Eureka’s return is very bloated

The technology stack in Eureke has been around for a while, but that doesn’t mean it’s not working anymore. I used this model a few years ago, and now it looks like I’m impressed