Make writing a habit together! This is the second day of my participation in the “Gold Digging Day New Plan · April More text challenge”. Click here for more details

Eureka Client startup process analysis

People, like nails, lose their bearings and start bending toward resistance, and they lose their reason for being — Landau

Core logic flow chart

The entire core logic of eureka client startup, such as what objects are required for initialization, ApplicationInfoManager, EurekaClientConfig, etc., as well as creating schedulers, thread pools, pulling registerssending heartbeats, etc.

Where to start

Go to the ExampleEurekaClient file and see the entire eureka-client project construction and initialization process



The main purpose of this class is to make a request to the Eureka-server after creating a Eureka-client

public class ExampleEurekaClient {
    private static ApplicationInfoManager applicationInfoManager;
    private static EurekaClient eurekaClient;
    // The eurekaClient is initialized to provide information for eurekaClient initialization
    private static synchronized ApplicationInfoManager
        initializeApplicationInfoManager(EurekaInstanceConfig instanceConfig) {
        InstanceInfo instanceInfo = 
                new EurekaConfigBasedInstanceInfoProvider(instanceConfig).get();
        applicationInfoManager = 
                new ApplicationInfoManager(instanceConfig, instanceInfo);
        return applicationInfoManager;
    }
    Initialize the eurekaClient client
    private static synchronized EurekaClient 
        initializeEurekaClient(ApplicationInfoManager applicationInfoManager, EurekaClientConfig clientConfig) {
        if (eurekaClient == null) {
            /* Create client */
            eurekaClient = new DiscoveryClient(applicationInfoManager, clientConfig);
        }
        return eurekaClient;
    }
    public static void main(String[] args) {
        ExampleEurekaClient sampleClient = new ExampleEurekaClient();
        // create the client loads a configuration file to create a reference information manager
        ApplicationInfoManager applicationInfoManager = 
            initializeApplicationInfoManager(new MyDataCenterInstanceConfig());
        /* Initialize the client */
        EurekaClient client = initializeEurekaClient(applicationInfoManager,
                                                     new DefaultEurekaClientConfig());
        // use the client
        sampleClient.sendRequestToServiceUsingEureka(client);
        // shutdown the clienteurekaClient.shutdown(); }}Copy the code

The new DiscoveryClient(applicationInfoManager, clientConfig) code is the real core, and we’ll dig into this class later

New DiscoveryClient core process

public class DiscoveryClient implements EurekaClient
Copy the code

DiscoveryClient implements the EurekaClient interface. This interface is used to back up the registry. If no registry is obtained, the registry logic will be used to back up the registry. However, it is usually not configured and the default value is null

public DiscoveryClient(ApplicationInfoManager applicationInfoManager, 
                    final EurekaClientConfig config, 
                    AbstractDiscoveryClientOptionalArgs args, 
                    EndpointRandomizer randomizer) {
    this(applicationInfoManager, config, args, new Provider<BackupRegistry>() {
        private volatile BackupRegistry backupRegistryInstance;
        @Override
        public synchronized BackupRegistry get(a) {
            if (backupRegistryInstance == null) { // Registry backup policy
                String backupRegistryClassName = config.getBackupRegistryImpl();
                if (backupRegistryInstance == null) {
                    backupRegistryInstance = newNotImplementedRegistryImpl(); }}return backupRegistryInstance;
        }
    }, randomizer);
}
Copy the code

1. Initialize the configuration

After the initial configuration of the parameters applicationInfoManager, config, etc., a local registry data is created locally, but this data is empty

this.applicationInfoManager = applicationInfoManager; // Incoming application configuration management
InstanceInfo myInfo = applicationInfoManager.getInfo();// Get InstanceInfo
clientConfig = config;// The client configuration passed in
staticClientConfig = clientConfig;
transportConfig = config.getTransportConfig(); // Client communication configuration
instanceInfo = myInfo;
appPathIdentifier = instanceInfo.getAppName() + "/" + instanceInfo.getId();
this.backupRegistryProvider = backupRegistryProvider;// Backup the registry
localRegionApps.set(new Applications());// Set all local registry information, initialize a null
Copy the code

2. Release unnecessary resources

If the current client does not need to register with the service and does not need to pull the registry, then some unused resources are released and returned

if(! config.shouldRegisterWithEureka() && ! config.shouldFetchRegistry()) { scheduler =null;
    heartbeatExecutor = null;
    cacheRefreshExecutor = null;
    eurekaTransport = null;
    instanceRegionChecker = newInstanceRegionChecker(...) ; DiscoveryManager.getInstance().setDiscoveryClient(this);
    DiscoveryManager.getInstance().setEurekaClientConfig(config);
    logger.info("Discovery Client initialized ... ");
    return;
}
Copy the code

3. Create core threads and schedulers

Create a thread scheduler with 2 core threads to schedule the following two thread pools, two thread pools, one is the heartbeat thread pool, one is the cache thread pool, the default number of core threads is 5, and these threads are daemon threads

// Core scheduler
scheduler = Executors.newScheduledThreadPool(2.new ThreadFactoryBuilder().
                setNameFormat("DiscoveryClient-%d").
                setDaemon(true).build()); // Set to daemon thread
/ / heartbeat thread pool clientConfig. GetHeartbeatExecutorThreadPoolSize () the default value is 5 * /
heartbeatExecutor = new ThreadPoolExecutor(
   1, clientConfig.getHeartbeatExecutorThreadPoolSize(), 0, 
   TimeUnit.SECONDS, new SynchronousQueue<Runnable>(),
   new ThreadFactoryBuilder().setNameFormat("DiscoveryClient-HeartbeatExecutor-%d").
   setDaemon(true).build()); // Set to daemon thread
/ / cache refresh thread pool clientConfig. GetCacheRefreshExecutorThreadPoolSize () the default value is 5
cacheRefreshExecutor = new ThreadPoolExecutor(
    1, clientConfig.getCacheRefreshExecutorThreadPoolSize(), 0, 
    TimeUnit.SECONDS, new SynchronousQueue<Runnable>(),
    new ThreadFactoryBuilder().setNameFormat("DiscoveryClient-CacheRefreshExecutor-%d").
    setDaemon(true).build()); // Set to daemon thread
Copy the code

4. Create components that support underlying communication

ScheduleServerEndpointTask (eurekaTransport, args) method which creates two client, if need to register, then create a registered client, if need to pull the registry then create a pull the registry of the client

eurekaTransport = new EurekaTransport();
scheduleServerEndpointTask(eurekaTransport, args);
// Create a registered client if necessary
if (clientConfig.shouldRegisterWithEureka()) {
    EurekaHttpClientFactory newRegistrationClientFactory = null;
    EurekaHttpClient newRegistrationClient = null;
    // Register the client factory
    newRegistrationClientFactory = EurekaHttpClients.registrationClientFactory(
                eurekaTransport.bootstrapResolver,
                eurekaTransport.transportClientFactory,
                transportConfig);
    // Create a registered client
    newRegistrationClient = newRegistrationClientFactory.newClient();
    eurekaTransport.registrationClientFactory = newRegistrationClientFactory;
    eurekaTransport.registrationClient = newRegistrationClient;
}
// If you need to pull the registry
if (clientConfig.shouldFetchRegistry()) {
    EurekaHttpClientFactory newQueryClientFactory = null;
    EurekaHttpClient newQueryClient = null;
    // Pull the client factory for the registry
    newQueryClientFactory = EurekaHttpClients.queryClientFactory(
            eurekaTransport.bootstrapResolver,
            eurekaTransport.transportClientFactory,
            clientConfig,
            transportConfig,
            applicationInfoManager.getInfo(),
            applicationsSource,
            endpointRandomizer);
    // Create a client to pull the registry
    newQueryClient = newQueryClientFactory.newClient();
    eurekaTransport.queryClientFactory = newQueryClientFactory;
    eurekaTransport.queryClient = newQueryClient;
}
Copy the code

5. Pull the core registry logic

Pull the registry logic, incremental pull and full amount pull two logical, if the local registry is empty or start for the first time or no configuration under the condition of incremental pull is full quantity to pull away, in addition to the above conditions are almost all incremental pull, but the whole process of the pull of a subsequent article we will explain in detail

if (clientConfig.shouldFetchRegistry()) {
   // The client pulls the registry core logic
   boolean primaryFetchRegistryResult = fetchRegistry(false);
}
Copy the code

6. Initialize the scheduling task

Initialize scheduling tasks, periodically pull the registry, and register services with eureka-server

initScheduledTasks();
Copy the code

Let’s go into this method and see what’s inside. This is the thread pool that it defined and the scheduler, ** New CacheRefreshThread(); ** New HeartbeatThread();

private void initScheduledTasks(a) {
    /** By default, 30 seconds */ is captured
    // If you need to pull the registry
    int registryFetchIntervalSeconds = clientConfig.getRegistryFetchIntervalSeconds();
    int expBackOffBound = clientConfig.getCacheRefreshExecutorExponentialBackOffBound();
    cacheRefreshTask = new TimedSupervisorTask(
            "cacheRefresh",
            scheduler,
            cacheRefreshExecutor,
            registryFetchIntervalSeconds,
            TimeUnit.SECONDS,
            expBackOffBound,
            new CacheRefreshThread()
    );
    /** Scheduling thread pool */
    scheduler.schedule(cacheRefreshTask, registryFetchIntervalSeconds, TimeUnit.SECONDS);
    // If you need to register
    /** Sends a heartbeat by default for 30s */
    int renewalIntervalInSecs = instanceInfo.getLeaseInfo().getRenewalIntervalInSecs();
    int expBackOffBound = clientConfig.getHeartbeatExecutorExponentialBackOffBound();
    // Heartbeat timer
    heartbeatTask = new TimedSupervisorTask(
            "heartbeat",
            scheduler,
            heartbeatExecutor,
            renewalIntervalInSecs,
            TimeUnit.SECONDS,
            expBackOffBound,
            new HeartbeatThread()
    );
    /** Scheduling thread pool */
    scheduler.schedule(heartbeatTask, renewalIntervalInSecs, TimeUnit.SECONDS);
}
Copy the code

The rest of the code is irrelevant, so we’ve initialized it so far. How to pull the registry and send the heartbeat logic will be covered in a separate article later

summary

When the client is initialized, it mainly does these things:

  1. Initial phone configuration information
  2. Release unnecessary resources
  3. Create a scheduler, two thread pools, one for the heartbeat thread pool, and one for the thread pool that pulls the registry to update the local cache. Default is 30s, and both are daemon threads
  4. Create the underlying communication component, pull registry communication component, and the communication component that sends the heartbeat
  5. Example Initialize the scheduling task

Eureka Server startup process analysis