Dubbo implementation principle and source code analysis — Fine collection Netty implementation principle and source code analysis — boutique collection
“Spring Implementation principles and source code analysis — Boutique Collection” MyBatis implementation principle and source code analysis — boutique collection
Spring MVC Implementation Principle and source code Analysis — A collection of fine works Database Entity Design Collection
Spring Boot Implementation principle and source code analysis — Boutique Collection Java Interview Questions + Java Learning Guide

Abstract: the original source www.iocoder.cn/Eureka/tran… “Taro source” welcome to reprint, keep the summary, thank you!

This article is mainly based on Eureka version 1.8.X

  • 1. An overview of the
  • 2. EurekaHttpClient
    • 2.1 EurekaJerseyClientImpl
    • 2.2 EurekaJerseyClientBuilder
  • 3. EurekaHttpClient
    • 3.1 EurekaHttpResponse
    • 3.2 TransportClientFactory
  • 4. AbstractJerseyEurekaHttpClient
    • 4.1 JerseyApplicationClient
    • 4.2 JerseyReplicationClient
  • 5. EurekaHttpClientDecorator
    • 5.1 MetricsCollectingEurekaHttpClient
    • 5.2 RedirectingEurekaHttpClient
    • 5.3 RetryableEurekaHttpClient
    • 5.4 SessionedEurekaHttpClient
  • 6. Create a network communication client
  • 666. The eggs


1. An overview of the

This article mainly shares the network communication part of Eureka. Without considering the compatibility of Eureka 2.x, the main two parts of Eureka 1.x network communication:

  • Eureka-client Requests network communication with the Eureka-server
  • In a Eureka-server cluster, a Eureka-Server requests network communication from other Eureka-Servers

This paper involves class at com.net flix. Discovery. Shared. The transport package, involves the main body of the class class diagram (open a larger view) as follows:

  • Pink — EurekaJerseyClient, Jersey client package for Eureka-Server based on Jersey Server.
  • The green part — EurekaHttpClient, eureka-Server HTTP access client, defines the specific eureka-server API call methods. If DiscoveryClient is analogous to a Service, EurekaHttpClient can be analogous to a city Dao.
  • The EurekaHttpClient implementation class implements the specific Eureka-Server API call methods.
  • The EurekaHttpClient delegate class, shown in red, provides features such as sessions, retries, redirects, and monitoring indicator collection.
  • The yellow part, EurekaHttpClientFactory, is used to create EurekaHttpClient.

The class diagram looks complicated, and the overall call relationship looks like this (open the larger image) :

Okay, let’s break it down layer by layer. Let’s get high.

Recommended Spring Cloud books:

  • Please support the legal version. Download piracy, is equal to the initiative to write low-level bugs.
  • DD — Spring Cloud Micro Services
  • Zhou Li — “Spring Cloud and Docker Micro-service Architecture Combat”
  • Buy two books together, jingdong free delivery.

2. EurekaHttpClient

Com.net flix. Discovery. Shared. Transport. Jersey. EurekaJerseyClient, EurekaHttpClient interface. The interface code is as follows:

public interface EurekaJerseyClient {

    ApacheHttpClient4 getClient();
    
    void destroyResources();
}
Copy the code
  • com.sun.jersey.client.apache4.ApacheHttpClient4Jersey Client based on Apache HttpClient4 implementation.

2.1 EurekaJerseyClientImpl

Com.net flix. Discovery. Shared. Transport. Jersey. EurekaJerseyClientImpl, EurekaHttpClient implementation class. The implementation code is as follows:

Public class EurekaJerseyClientImpl implements EurekaJerseyClient {** ** JerseyClient */ based on Apache HttpClient4 private final ApacheHttpClient4 apacheHttpClient; / * * * the Apache HttpClient free connection sweeper * / private final ApacheHttpClientConnectionCleaner ApacheHttpClientConnectionCleaner; /** * Jersey Client config */ Jersey Client config; public EurekaJerseyClientImpl(int connectionTimeout, int readTimeout, final int connectionIdleTimeout, ClientConfig clientConfig) { try { jerseyClientConfig = clientConfig; ApacheHttpClient = apacheHttpClient4.create (jerseyClientConfig); / / set the connection parameters HttpParams params = apacheHttpClient. GetClientHandler (). The getHttpClient () getParams (); HttpConnectionParams.setConnectionTimeout(params, connectionTimeout); HttpConnectionParams.setSoTimeout(params, readTimeout); / / create ApacheHttpClientConnectionCleaner enclosing ApacheHttpClientConnectionCleaner = new ApacheHttpClientConnectionCleaner(apacheHttpClient, connectionIdleTimeout); } catch (Throwable e) { throw new RuntimeException("Cannot create Jersey client", e); } } @Override public ApacheHttpClient4 getClient() { return apacheHttpClient; } @Override public void destroyResources() { apacheHttpClientConnectionCleaner.shutdown(); apacheHttpClient.destroy(); }}Copy the code

2.2 EurekaJerseyClientBuilder

EurekaJerseyClientBuilder EurekaJerseyClientImpl inner class, used to create EurekaJerseyClientImpl.

Call #build() to create EurekaJerseyClientImpl with the following code:

// EurekaJerseyClientBuilder.java public EurekaJerseyClient build() { MyDefaultApacheHttpClient4Config config = new MyDefaultApacheHttpClient4Config(); try { return new EurekaJerseyClientImpl(connectionTimeout, readTimeout, connectionIdleTimeout, config); } catch (Throwable e) { throw new RuntimeException("Cannot create Jersey client ", e); }}Copy the code

3. EurekaHttpClient

Com.net flix. Discovery. Shared. Transport. EurekaHttpClient, Eureka – HTTP access client Server, define the specific methods of Eureka – Server API calls. Click on the link to view EurekaHttpClient with Chinese annotations.

3.1 EurekaHttpResponse

Com.net flix. Discovery. Shared. Transport. EurekaHttpResponse, request and response objects, the implementation code is as follows:

Public class EurekaHttpResponse<T> {/** * return statusCode */ private final int statusCode; /** * return object (Entity) */ private final T Entity; /** * Return header */ private final Map<String, String> headers; /** * private final URI location; / /... Omit setting/getting and Builder}Copy the code

3.2 TransportClientFactory

Com.net flix. Discovery. Shared. Transport. TransportClientFactory, create EurekaHttpClient factory interface. The interface code is as follows:

Public interface TransportClientFactory {/** * Create EurekaHttpClient ** @param serviceUrl Eureka-server address * @return EurekaHttpClient */ EurekaHttpClient newClient(EurekaEndpoint serviceUrl); /** */ void shutdown(); }Copy the code

Most EurekaHttpClient implementation classes have their corresponding factory implementation classes.

4. AbstractJerseyEurekaHttpClient

Com.net flix. Discovery. Shared. Transport. Jersey. AbstractJerseyEurekaHttpClient, realize EurekaHttpClient abstract class, The actual implementation of the specific Eureka-Server API call method. The implementation code is as follows:

1: public abstract class AbstractJerseyEurekaHttpClient implements EurekaHttpClient { 2: 3: private static final Logger logger = LoggerFactory.getLogger(AbstractJerseyEurekaHttpClient.class); 4: 5: /** 6: * Jersey Client 7: */ 8: protected final Client jerseyClient; 9: /** 10: * requested eureka-server 11: */ 12: protected final String serviceUrl; 13: 14: protected AbstractJerseyEurekaHttpClient(Client jerseyClient, String serviceUrl) { 15: this.jerseyClient = jerseyClient; 16: this.serviceUrl = serviceUrl; 17: logger.debug("Created client for url: {}", serviceUrl); } 19: 20: @override 21: public EurekaHttpResponse<Void> register(InstanceInfo info) {22: // set the request address. String urlPath = "apps/" + info.getAppName(); 24: ClientResponse response = null; 25: try { 26: Builder resourceBuilder = jerseyClient.resource(serviceUrl).path(urlPath).getRequestBuilder(); 27: // Set the request header. 28: addExtraHeaders(resourceBuilder); Eureka-server 30: Response = resourceBuilder 31:. Header (" accept-encoding ", "gzip") // gzip 32: .type(MediaType.APPLICATION_JSON_TYPE) // Request parameter format JSON 33:.accept(MediaType.APPLICATION_JSON) // Response result format JSON 34: .post(ClientResponse.class, info); Create EurekaHttpResponse 36: return anEurekaHttpResponse(response.getStatus()).headers(headersOf(response)).build(); 37: } finally { 38: if (logger.isDebugEnabled()) { 39: logger.debug("Jersey HTTP POST {}/{} with instance {}; statusCode={}", serviceUrl, urlPath, info.getId(), 40: response == null ? "N/A" : response.getStatus()); 41: } 42: if (response ! = null) { 43: response.close(); 44:} 45:} 46:}Copy the code
  • jerseyClientProperty, Jersey Client, used aboveEurekaHttpClient#getClient(...)Method to get ApacheHttpClient4.
  • serviceUrlProperty, the requested Eureka-server address.
  • #register() method to register application instances with Eureka-server. The other methods have similar code.

    • Lines 22 through 26: Set the request address.
    • Line 28: Call #addExtraHeaders(…) Method to set the request header. This method is abstract and provides subclasses to implement custom request headers. The code is as follows:

      protected abstract void addExtraHeaders(Builder webResource);
      Copy the code
      • x
        • Lines 29 through 34: Request eureka-server.
        • Lines 35 through 36: Parse the response result and create EurekaHttpResponse.

4.1 JerseyApplicationClient

Com.net flix. Discovery. Shared. Transport. Jersey. JerseyApplicationClient, realize the Eureka – Client request had been – Server network communication. Click the link to view the JerseyApplicationClient with Chinese annotations.

4.1.1 JerseyEurekaHttpClientFactory

Com.net flix. Discovery. Shared. Transport. Jersey. JerseyEurekaHttpClientFactory, create JerseyApplicationClient factory class. The implementation code is as follows:

public class JerseyEurekaHttpClientFactory implements TransportClientFactory {
    
    private final EurekaJerseyClient jerseyClient;
    private final ApacheHttpClient4 apacheClient;
    private final ApacheHttpClientConnectionCleaner cleaner;
    private final Map<String, String> additionalHeaders;
    
    public JerseyEurekaHttpClientFactory(ApacheHttpClient4 apacheClient, long connectionIdleTimeout, Map<String, String> additionalHeaders) {
        this(null, apacheClient, connectionIdleTimeout, additionalHeaders);
    }

    private JerseyEurekaHttpClientFactory(EurekaJerseyClient jerseyClient,
                                          ApacheHttpClient4 apacheClient,
                                          long connectionIdleTimeout,
                                          Map<String, String> additionalHeaders) {
        this.jerseyClient = jerseyClient;
        this.apacheClient = jerseyClient != null ? jerseyClient.getClient() : apacheClient;
        this.additionalHeaders = additionalHeaders;
        this.cleaner = new ApacheHttpClientConnectionCleaner(this.apacheClient, connectionIdleTimeout);
    }
    
    @Override
    public EurekaHttpClient newClient(EurekaEndpoint endpoint) {
        return new JerseyApplicationClient(apacheClient, endpoint.getServiceUrl(), additionalHeaders);
    }

    @Override
    public void shutdown() {
        cleaner.shutdown();
        if (jerseyClient != null) {
            jerseyClient.destroyResources();
        } else {
            apacheClient.destroy();
        }
    }
}
Copy the code

4.1.2 JerseyEurekaHttpClientFactoryBuilder

JerseyEurekaHttpClientFactoryBuilder JerseyEurekaHttpClientFactory inner class, used to create JerseyEurekaHttpClientFactory. Click on the link to see JerseyEurekaHttpClientFactory with Chinese annotations.

Call JerseyEurekaHttpClientFactory# create (…). Methods, create JerseyEurekaHttpClientFactory, implementation code is as follows:

public static JerseyEurekaHttpClientFactory create(EurekaClientConfig clientConfig, Collection<ClientFilter> additionalFilters, InstanceInfo myInstanceInfo, AbstractEurekaIdentity clientIdentity) { JerseyEurekaHttpClientFactoryBuilder clientBuilder = newBuilder() .withAdditionalFilters(additionalFilters) // Client addfilters. WithMyInstanceInfo (myInstanceInfo) // Application instance .withUserAgent("Java-EurekaClient") // UA .withClientConfig(clientConfig) .withClientIdentity(clientIdentity); // Set Client Name if ("true".equals(System.getProperty("com.netflix.eureka.shouldSSLConnectionsUseSystemSocketFactory"))) { clientBuilder.withClientName("DiscoveryClient-HTTPClient-System").withSystemSSLConfiguration(); } else if (clientConfig.getProxyHost() ! = null && clientConfig.getProxyPort() ! = null) { clientBuilder.withClientName("Proxy-DiscoveryClient-HTTPClient") .withProxy( clientConfig.getProxyHost(), Integer.parseInt(clientConfig.getProxyPort()), clientConfig.getProxyUserName(), clientConfig.getProxyPassword() ); // http proxy } else { clientBuilder.withClientName("DiscoveryClient-HTTPClient"); } return clientBuilder.build(); } public static JerseyEurekaHttpClientFactoryBuilder newBuilder() { return new JerseyEurekaHttpClientFactoryBuilder().withExperimental(false); }Copy the code

4.2 JerseyReplicationClient

Com.net flix. Eureka. Transport. JerseyReplicationClient, eureka – Server cluster, eureka – Server request other eureka – Server network communication.

  • Implement AbstractJerseyEurekaHttpClient# addExtraHeaders () method, add custom head x – netflix – discovery – the replication = true, the code is as follows:

    @Override
    protected void addExtraHeaders(Builder webResource) {
       webResource.header(PeerEurekaNode.HEADER_REPLICATION, "true");
    }
    Copy the code
  • Rewrite the # sendHeartBeat (…). Method, in “Eureka source code parsing – Eureka-Server cluster synchronization” have detailed analysis.

  • implementationcom.netflix.eureka.cluster.HttpReplicationClientInterface, implemented#submitBatchUpdates(...)Methods,Eureka — Eureka-Server cluster SynchronizationHave detailed analysis.

4.2.1 No factory

JerseyReplicationClient does not have its own factory.

Call JerseyReplicationClient# createReplicationClient (…). Static method to create JerseyReplicationClient. Click the link to see the method code annotated in Chinese.

5. EurekaHttpClientDecorator

Com.net flix. Discovery. Shared. Transport. Decorators. EurekaHttpClientDecorator, EurekaHttpClient consignor abstract classes. The implementation code is as follows:

Public abstract class EurekaHttpClientDecorator implements EurekaHttpClient {/ execution request * * * * * @ param requestExecutor RequestExecutor * @param <R> request generic * @return response */ protected abstract <R> EurekaHttpResponse<R> execute requestExecutor); @Override public EurekaHttpResponse<Void> register(final InstanceInfo info) { return execute(new RequestExecutor<Void>()  { @Override public EurekaHttpResponse<Void> execute(EurekaHttpClient delegate) { return delegate.register(info); } @Override public RequestType getRequestType() { return RequestType.Register; }}); }}Copy the code
  • #execute(...) Abstract methods, subclasses implement the method to implement their own features.
  • #register()Method to register application instances with Eureka-server.The other methods have similar code.
    • call#execute(...)Method and pass in the original registration implementation through the RequestExecutor.
    • Subclasses in the implementation#execute(...)Method can be calledRequestExecutor#execute(...)Method to continue with the original logic.
    • Refer to design patterns: Design Patterns (19) Template Method (Class behavior).
  • RequestType, an enumeration class of request types. The code is as follows:

    // EurekaHttpClientDecorator.java
    public enum RequestType {
       Register,
       Cancel,
       SendHeartBeat,
       StatusUpdate,
       DeleteStatusOverride,
       GetApplications,
       GetDelta,
       GetVip,
       GetSecureVip,
       GetApplication,
       GetInstance,
       GetApplicationInstance
    }
    Copy the code
  • RequestExecutor, RequestExecutor interface. The interface code is as follows:

    / / EurekaHttpClientDecorator. Java public interface RequestExecutor < R > {/ execution request * * * * * @ param entrusted by the delegate EurekaHttpClient * @return response */ EurekaHttpResponse<R> execute(EurekaHttpClient delegate); /** * @requestType getRequestType(); }Copy the code

Delegation is a basic skill in software design patterns. In the delegate pattern, two objects participate in processing the same request, and the receiving object delegates the request to the other object. The delegate pattern is a basic skill, and many other patterns, such as the state pattern, the policy pattern, and the visitor pattern, essentially use the delegate pattern for more specific situations. The delegate pattern allows us to substitute aggregation for inheritance, and it also allows us to simulate mixins.

On the basis of the above picture, we add the delegate relationship as shown below (open the larger picture) :

  • Note that each delegate implementation class may have a property of type EurekaHttpClientFactory on it to create its delegate’s EurekaHttpClient. Why is there a Factory? RetryableEurekaHttpClient retry the request, for example, multiple Eureka – Server addresses, each had been – will create a EurekaHttpClient Server address. Therefore, you need to understand the following references to EurekaHttpClientFactory and the delegated EurekaHttpClient.

5.1 MetricsCollectingEurekaHttpClient

Com.net flix. Discovery. Shared. Transport. Decorators. MetricsCollectingEurekaHttpClient, collect EurekaHttpClient monitoring indicators, Cooperate with Netflix Servo to realize monitoring information collection.

#execute()

1: @Override 2: protected <R> EurekaHttpResponse<R> execute(RequestExecutor<R> requestExecutor) { 3: // Get the request indicator 4 of the request type: EurekaHttpClientRequestMetrics requestMetrics = metricsByRequestType.get(requestExecutor.getRequestType()); 5: Stopwatch stopwatch = requestMetrics.latencyTimer.start(); 6: try {7: // execute request 8: EurekaHttpResponse<R> httpResponse = requestExecutor.execute(delegate); 9: / / increase request target 10: requestMetrics. CountersByStatus. Get (mappedStatus (httpResponse)). The increment (); 11: return httpResponse; 12: } catch (Exception e) { 13: requestMetrics.connectionErrors.increment(); 14: exceptionsMetric.count(e); 15: throw e; 16: } finally { 17: stopwatch.stop(); 19:18:}}Copy the code
  • Line 10: CallRequestExecutor#execute(...)Method to proceed with the request.
    • delegateProperty corresponding to JerseyApplicationClient.

5.2 RedirectingEurekaHttpClient

Com.net flix. Discovery. Shared. Transport. Decorators. RedirectingEurekaHttpClient, Find EurekaHttpClient for non-302 redirected Eureka-Server.

#execute()

1: @Override 2: protected <R> EurekaHttpResponse<R> execute(RequestExecutor<R> requestExecutor) { 3: EurekaHttpClient currentEurekaClient = delegateRef.get(); 4: if (currentEurekaClient == null) {// No non-302 eureka-server 5 is found: AtomicReference<EurekaHttpClient> currentEurekaClientRef = new AtomicReference<>(factory.newClient(serviceEndpoint)); 6: try { 7: EurekaHttpResponse<R> response = executeOnNewServer(requestExecutor, currentEurekaClientRef); 8: // Close the original delegate EurekaHttpClient and set the current successful non-302 request EurekaHttpClient 9: // Close the original delegate EurekaHttpClient and set the current successful non-302 request EurekaHttpClient 9: TransportUtils.shutdown(delegateRef.getAndSet(currentEurekaClientRef.get())); 10: return response; 11: } catch (Exception e) { 12: logger.error("Request execution error", e); 13: TransportUtils.shutdown(currentEurekaClientRef.get()); 14: throw e; 15:} 16:} else {// A non-302 Eureka-server 17: try {18: return requestExecutor.execute(currentEurekaClient); 19: } catch (Exception e) { 20: logger.error("Request execution error", e); 21: delegateRef.compareAndSet(currentEurekaClient, null); 22: currentEurekaClient.shutdown(); 23: throw e; 24:} 25:} 26:}Copy the code
  • Note: This is different from normal 302 status return processing as we understand it!!
  • It is divided into two parts: [lines 4 to 15] and [lines 16 to 24].
    • The former means that no eureka-server was found that did not return the 302 status code, which was passed in in the originalserviceUrlsExecute the request to find the Eureka-server that is not returned by the 302 status code.
      • When a non-302 status code is returned, find the Eureka-server that does not return the 302 status code.
      • When the 302 status code is returned, the request is executed to the newly redirected Eureka-server until it is successfully found or the maximum number of times is exceeded.
        • The latter, which means that a Eureka-server that does not return a 302 status code has been found, executes the request directly. Note: The Eureka-server returns the 302 status code and does not process any more.
        • Eureka 2. x TODO[0028] : Write cluster and read cluster.
  • Line 5: Use the originalserviceEndpoint(the equivalent ofserviceUrls) to create aentrustEurekaHttpClient.
  • Line 7: Call#executeOnNewServer(...)Method to find the Eureka-server that is not returned by the 302 status code by executing the request. Implement the code, clicklinkView the code implementation with Chinese comments.
  • [former] [former] Line 9: Close the originaldelegateRef(Because of possible concurrency here, multiple threads have found the eureka-server returned by non-302 status codes) and set the current successful non-302 request to the EurekaHttpClientdelegateRef.
  • Line 13: ClosecurrentEurekaClientRefWhen an exception occurs or the maximum number of redirects is exceeded.
  • Line 18: Indicates that a eureka-server that does not return a 302 status code has been found, and the request is executed directly.
  • [latter] Lines 21 to 22: Execute request exception, closecurrentEurekaClient, the eureka-server must return the 302 status code again.

5.2.1 factory

RedirectingEurekaHttpClient provide # createFactory (…). The static method gets the factory that created it, click the link to see.

5.3 RetryableEurekaHttpClient

Com.net flix. Discovery. Shared. Transport. Decorators. RetryableEurekaHttpClient, support to multiple Eureka – EurekaHttpClient Server request retry.

#execute()

1: @Override 2: protected <R> EurekaHttpResponse<R> execute(RequestExecutor<R> requestExecutor) { 3: List<EurekaEndpoint> candidateHosts = null; 4: int endpointIdx = 0; 5: for (int retry = 0; retry < numberOfRetries; retry++) { 6: EurekaHttpClient currentHttpClient = delegate.get(); 7: EurekaEndpoint currentEndpoint = null; 10: if (currentHttpClient == NULL) {11: // Get the candidate eureka-server address array 12: if (candidateHosts == null) { 13: candidateHosts = getHostCandidates(); 14: if (candidateHosts.isEmpty()) { 15: throw new TransportException("There is no known eureka server; cluster server list is empty"); 20: if (endpointIdx >= candidateHosts.size()) {21: if (endpointIdx >= candidateHosts.size()) {21: throw new TransportException("Cannot execute request on any known server"); 22:} 23: 24: // create a candidate EurekaHttpClient 25: currentEndpoint = candidatehosts. get(endpointIdx++); 26: currentHttpClient = clientFactory.newClient(currentEndpoint); 27:} 28: 29: try {30: // Execute request 31: EurekaHttpResponse<R> Response = RequestExecutor. execute(currentHttpClient); 32: // Determine whether the corresponding is acceptable, if so, return. 33: if (serverStatusEvaluator.accept(response.getStatusCode(), requestExecutor.getRequestType())) { 34: delegate.set(currentHttpClient); 35: if (retry > 0) { 36: logger.info("Request execution succeeded on retry #{}", retry); 37: } 38: return response; 39: } 40: logger.warn("Request execution failure with status code {}; retrying on another server if available", response.getStatusCode()); 41: } catch (Exception e) { 42: logger.warn("Request execution failed with message: {}", e.getMessage()); // just log message as the underlying client should log the stacktrace 43: } 44: 45: // The request fails. If currentHttpClient, clear delegate 46: // Connection error or 5xx from the server that must be retried on another server 47: delegate.compareAndSet(currentHttpClient, null); 48: 49: // Request failed to add currentEndpoint to isolated collection 50: if (currentEndpoint! = null) { 51: quarantineSet.add(currentEndpoint); 52: } 53: } 54: throw new TransportException("Retry limit reached; giving up on completing the request"); 55:}Copy the code
  • Line 10: CurrentcurrentHttpClientNonexistence means originaldelegateNo EurekaHttpClient successfully requested to Eureka-Server exists.
    • In this case, you need to retry the request from the eureka-Server array in the configuration to obtain the eureka-Server that can be requested.
    • If there is already a successful requestdelegateDirectly use it to perform the request.
  • Lines 11 through 17: Call the #getHostCandidates() method to obtain the array of candidate Eureka-Server serviceUrls. The implementation code is as follows:

    1: private List<EurekaEndpoint> getHostCandidates() {2: // Obtain the candidate Eureka-Server address array 3: List<EurekaEndpoint> candidateHosts = clusterResolver.getClusterEndpoints(); 4: 5: // Hold intersection (remove element from quarantineSet not in candidateHosts) 6: quarantineSet. RetainAll (candidateHosts); 7: 8: // In order to ensure the minimum available candidate eureka-server address array, remove element 9 in the isolated set: // If enough hosts are bad, we have no choice but start over again 10: int threshold = (int) (candidateHosts.size() * transportConfig.getRetryableClientQuarantineRefreshPercentage()); // 0.66 11: If (quarantineset.isempty ()) {12: // no-op 13:} else if (quarantineset.size () >= threshold) {14: logger.debug("Clearing quarantined list of size {}", quarantineSet.size()); 15: quarantineSet.clear(); 16: } else { 17: List<EurekaEndpoint> remainingHosts = new ArrayList<>(candidateHosts.size()); 18: for (EurekaEndpoint endpoint : candidateHosts) { 19: if (! quarantineSet.contains(endpoint)) { 20: remainingHosts.add(endpoint); 21: } 22: } 23: candidateHosts = remainingHosts; 24: } 25: 26: return candidateHosts; 27:}Copy the code
    • Line 3: CallClusterResolver#getClusterEndpoints()Method to obtain the candidate Eureka-server address array (candidateHosts).Pay attention to: This method returns an array of Eureka-server addresses, using the local IP address asA random seed, so that application instances with different IP addresses can obtain different array order, while application instances with the same IP address can obtain the same array order.The effect is similar to that of the load balancing algorithm based on IP HASH. The code to implement this feature is in”ResolverUtils#randomize(…)” Eureka source code parsing — EndPoint and parserDetailed analysis.
    • Line 6: CallSet#retainAll()Method to remove the isolated failed Eureka-server address array (quarantineSet) is notcandidateHostsThe element.
    • Lines 8 through 24: guaranteed minimum availablecandidateHostsRemove inquarantineSetThe element.
      • Line 10: Minimum available threshold, configurationeureka.retryableClientQuarantineRefreshPercentageTo set the percentage. Default:0.66
      • Lines 13 to 15 at most:quarantineSetQuantity exceeds threshold, emptyquarantineSetAll,candidateHostsTry again.
      • Lines 17 to 24:quarantineSetIf the quantity does not exceed the threshold, removecandidateHostsIn thequarantineSetThe element.
  • Lines 19 to 22: When the candidateHosts limit is exceeded, all eureka-server requests fail and an exception is thrown.

  • Lines 24 through 26: Create the delegate EurekaHttpClient for the following request execution.
  • Line 31: Execute the request.
  • Line 33: Call the ServerStatusEvaluator# Accept () method to determine whether the response status code and request type are acceptable. The implementation code is as follows:

    // ServerStatusEvaluators.java private static final ServerStatusEvaluator LEGACY_EVALUATOR = new ServerStatusEvaluator()  { @Override public boolean accept(int statusCode, RequestType requestType) { if (statusCode >= 200 && statusCode < 300 || statusCode == 302) { return true; } else if (requestType == requestType.register && statusCode == 404) {return true; } else if (requestType = = requestType SendHeartBeat && statusCode = = 404) {/ / heartbeat, 404 acceptable return true; } else if (requestType == requesttype.cancel) {// Cancel is best effort offline, accept all return true; } else if (requestType = = requestType GetDelta && (statusCode = = 403 | | statusCode = = 404)) {/ / increment for registration information, 403 404 Acceptable return true; } return false; }};Copy the code
  • Line 34: Request successful, set delegate. On the next request, use the delegate first, and retry with the candidate Eureka-server address array if the request fails.

  • Line 47: Request failed,delegateIf it is equal to thecurrentHttpClientTo clear the file.
  • Lines 50 to 52: The request fails, add the requested Eureka-server address toquarantineSet
  • To sum up:
    • If there is a successful request to EurekaHttpClient, continue using it. If the request fails, perform Step 2.
    • If there is no successful EurekaHttpClient request, obtain the candidate Eureka-Server address array and create a new EurekaHttpClient in sequence until it succeeds or the maximum number of retries is exceeded. When the request succeeds, save the EurekaHttpClient and continue to use it next time until the request fails.

5.3.1 factory

RetryableEurekaHttpClient provide # createFactory (…). The static method gets the factory that created it, click the link to see.

5.4 SessionedEurekaHttpClient

Com.net flix. Discovery. Shared. Transport. Decorators. SessionedEurekaHttpClient, support EurekaHttpClient session. Perform regular re-establishment sessions to prevent a Eureka-client from forever connecting to only one particular Eureka-server. This, in turn, ensures load balancing between Eureka-client and Eureka-Server connections when the Eureka-Server cluster changes.

#execute(…) , the code is as follows:

1: @Override 2: protected <R> EurekaHttpResponse<R> execute(RequestExecutor<R> requestExecutor) { 3: long now = System.currentTimeMillis(); 4: long delay = now - lastReconnectTimeStamp; 5: 6: // Closes the current delegate EurekaHttpClient when the current session time expires. 7: if (delay >= currentSessionDurationMs) { 8: logger.debug("Ending a session and starting anew"); 9: lastReconnectTimeStamp = now; 10: currentSessionDurationMs = randomizeSessionDuration(sessionDurationMs); 11: TransportUtils.shutdown(eurekaHttpClientRef.getAndSet(null)); } 13: 14: // Get the delegate EurekaHttpClient. If not, create a new delegate EurekaHttpClient. 15: EurekaHttpClient eurekaHttpClient = eurekaHttpClientRef.get(); 16: if (eurekaHttpClient == null) { 17: eurekaHttpClient = TransportUtils.getOrSetAnotherClient(eurekaHttpClientRef, clientFactory.newClient()); 18: } 19: return requestExecutor.execute(eurekaHttpClient); 20:}Copy the code
  • Lines 7 through 12: When the current session time expires, close the currently delegated EurekaHttpClient.

    • Line 10: Call #randomizeSessionDuration(…) Method, calculate the next session timeout duration, the formula is sessionDurationMs * (0.5, 1.5), the code is as follows:

      protected long randomizeSessionDuration(long sessionDurationMs) {
         long delta = (long) (sessionDurationMs * (random.nextDouble() - 0.5));
         return sessionDurationMs + delta;
      }
      Copy the code
      • The randomness of session expiration is increased to make the reconnection time of all eureka-client sessions more discrete, avoiding the centralized expiration time. TODO[0028] : Write cluster and read cluster, return 302. Associated 1.x new Transport Enhancements.
  • Lines 15 to 18: Get the delegate EurekaHttpClient. If not, create a new delegate EurekaHttpClient. TransportUtils#getOrSetAnotherClient(…) The method code is as follows:

    1: public static EurekaHttpClient getOrSetAnotherClient(AtomicReference<EurekaHttpClient> eurekaHttpClientRef, EurekaHttpClient another) { 2: EurekaHttpClient existing = eurekaHttpClientRef.get(); 3: / / is empty set 4: the if (eurekaHttpClientRef.com pareAndSet (null, another)) {5: return another; 6:} 7: // set failure, meaning another thread is set 8: another.shutdown(); 9: return existing; 10:}Copy the code
    • The method is implemented and obtainedeurekaHttpClientRefEurekaHttpClient. If not, willanotherSet it toeurekaHttpClientRef. When multiple threads are set, one and only one thread is set successfully, and the other thread fails, meaning that the currenteurekaHttpClientRefThere’s EurekaHttpClient, returneurekaHttpClientRef
    • Return eurekahttpclientref.get () eurekahttpClientref.get (); The simulation reproduced the BUG code as follows:

  • Line 19: Execute the request.

5.4.1 No factory

In SessionedEurekaHttpClient class does not implement to create its factories. In “6. Create network communication client” search canonicalClientFactory, you can see EurekaHttpClients#canonicalClientFactory(…) Method, internal SessionedEurekaHttpClient the creation of the factory.

6. Create a network communication client

For eureka-server, call JerseyReplicationClient#createReplicationClient(…) Static method can be used to create network communication clients for Eureka-Server to request other Eureka-Servers in eureka-Server cluster.

Eureka-client is divided into two different network communication clients for registering application instances (registrationClient) and querying registration information (newQueryClient). The code is as follows when DiscoveryClient is initialized:

// DiscoveryClient.class 1: private void scheduleServerEndpointTask(EurekaTransport eurekaTransport, 2: AbstractDiscoveryClientOptionalArgs args) { 3: 4: Collection<? > additionalFilters = args == null 5: ? Collections.emptyList() 6: : args.additionalFilters; 7: 8: EurekaJerseyClient providedJerseyClient = args == null 9: ? null 10: : args.eurekaJerseyClient; 11: 12: TransportClientFactories argsTransportClientFactories = null; 13: if (args ! = null && args.getTransportClientFactories() ! = null) { 14: argsTransportClientFactories = args.getTransportClientFactories(); 15: } 16: 17: // Ignore the raw types warnings since the client filter interface changed between jersey 1/2 18: @SuppressWarnings("rawtypes") 19: TransportClientFactories transportClientFactories = argsTransportClientFactories == null 20: ? new Jersey1TransportClientFactories() 21: : argsTransportClientFactories; 22: 23: // If the transport factory was not supplied with args, assume they are using jersey 1 for passivity 24: // noinspection unchecked 25: eurekaTransport.transportClientFactory = providedJerseyClient == null 26: ? transportClientFactories.newTransportClientFactory(clientConfig, additionalFilters, applicationInfoManager.getInfo()) 27: : transportClientFactories.newTransportClientFactory(additionalFilters, providedJerseyClient); 28:29: // (omit code) Initializes the application instance parser data source TODO[0028] writes to and reads from the cluster 30:31: // (omit code) Creates the EndPoint parser 32: eurekaTransport.bootstrapResolver = EurekaHttpClients.newBootstrapResolver(...) 33: 34: if (clientConfig.shouldRegisterWithEureka()) { 35: EurekaHttpClientFactory newRegistrationClientFactory = null; 36: EurekaHttpClient newRegistrationClient = null; 37: try { 38: newRegistrationClientFactory = EurekaHttpClients.registrationClientFactory( 39: eurekaTransport.bootstrapResolver, 40: eurekaTransport.transportClientFactory, 41: transportConfig 42: ); 43: newRegistrationClient = newRegistrationClientFactory.newClient(); 44: } catch (Exception e) { 45: logger.warn("Transport initialization failure", e); 46: } 47: eurekaTransport.registrationClientFactory = newRegistrationClientFactory; 48: eurekaTransport.registrationClient = newRegistrationClient; 49: } 50: 51: // new method (resolve from primary servers for read) 52: // Configure new transport layer (candidate for injecting in the future) 53: if (clientConfig.shouldFetchRegistry()) { 54: EurekaHttpClientFactory newQueryClientFactory = null; 55: EurekaHttpClient newQueryClient = null; 56: try { 57: newQueryClientFactory = EurekaHttpClients.queryClientFactory( 58: eurekaTransport.bootstrapResolver, 59: eurekaTransport.transportClientFactory, 60: clientConfig, 61: transportConfig, 62: applicationInfoManager.getInfo(), 63: applicationsSource 64: ); 65: newQueryClient = newQueryClientFactory.newClient(); 66: } catch (Exception e) { 67: logger.warn("Transport initialization failure", e); 68: } 69: eurekaTransport.queryClientFactory = newQueryClientFactory; 70: eurekaTransport.queryClient = newQueryClient; 71:} 72:}Copy the code
  • Line 18 to 27: call Jersey1TransportClientFactories # newTransportClientFactory (…). Method to create the EurekaHttpClientFactory delegate common to registrationClient and queryClient:

    // Jersey1TransportClientFactories.java public TransportClientFactory newTransportClientFactory(final EurekaClientConfig  clientConfig, final Collection<ClientFilter> additionalFilters, final InstanceInfo myInstanceInfo) { // JerseyEurekaHttpClientFactory final TransportClientFactory jerseyFactory = JerseyEurekaHttpClientFactory.create( clientConfig, additionalFilters, myInstanceInfo, new EurekaClientIdentity(myInstanceInfo.getIPAddr()) ); // TransportClientFactory final TransportClientFactory metricsFactory = MetricsCollectingEurekaHttpClient.createFactory(jerseyFactory); Return TransportClientFactory() {@override public EurekaHttpClient newClient(EurekaEndpoint serviceUrl) { return metricsFactory.newClient(serviceUrl); } @Override public void shutdown() { metricsFactory.shutdown(); jerseyFactory.shutdown(); }}; }Copy the code
    • In TransportClientFactory entrust JerseyEurekaHttpClientFactory.
  • Line 34 to 49: call EurekaHttpClients# registrationClientFactory (…). Method to create EurekaHttpClientFactory for registrationClient as follows:

    // EurekaHttpClients.java public static EurekaHttpClientFactory registrationClientFactory(ClusterResolver bootstrapResolver, TransportClientFactory transportClientFactory, EurekaTransportConfig transportConfig) { return canonicalClientFactory(EurekaClientNames.REGISTRATION, transportConfig, bootstrapResolver, transportClientFactory); } static EurekaHttpClientFactory canonicalClientFactory(final String name, final EurekaTransportConfig transportConfig, final ClusterResolver<EurekaEndpoint> clusterResolver, final TransportClientFactory transportClientFactory) { return new EurekaHttpClientFactory() { // SessionedEurekaHttpClientFactory @Override public EurekaHttpClient newClient() { return new SessionedEurekaHttpClient( name, RetryableEurekaHttpClient.createFactory( // RetryableEurekaHttpClient name, transportConfig, clusterResolver, RedirectingEurekaHttpClient.createFactory(transportClientFactory), // RedirectingEurekaHttpClient ServerStatusEvaluators.legacyEvaluator()), transportConfig.getSessionedClientReconnectIntervalSeconds() * 1000 ); } @Override public void shutdown() { wrapClosable(clusterResolver).shutdown(); }}; }Copy the code
  • Line 51 through 71: call EurekaHttpClients#queryClientFactory(…) Create the queryClient EurekaHttpClientFactory method as follows:

    // EurekaHttpClients.java public static EurekaHttpClientFactory queryClientFactory(ClusterResolver bootstrapResolver, TransportClientFactory transportClientFactory, EurekaClientConfig clientConfig, EurekaTransportConfig transportConfig, InstanceInfo myInstanceInfo, ApplicationsResolver.ApplicationsSource applicationsSource) { ClosableResolver queryResolver = transportConfig.useBootstrapResolverForQuery() ? wrapClosable(bootstrapResolver) : queryClientResolver(bootstrapResolver, transportClientFactory, clientConfig, transportConfig, myInstanceInfo, applicationsSource); return canonicalClientFactory(EurekaClientNames.QUERY, transportConfig, queryResolver, transportClientFactory); // This method has}Copy the code

666. The eggs

This is really an Easter egg, we will adjust the overall call relationship as follows (open the larger picture) :

Fat friend, did you learn?

Fat friends, share my public number (impression channel source code) to your fat friends?