The leader asked me to study Eureka source code | startup process

Keywords: Java Interview eureka

Startup process of Eureka source code

Hello, I'm Wukong.

Recently, I was tossing Eureka source code. Because the environment is too voluminous, I have to roll up the source code. In addition, being able to read the source code of open source projects and solve the problems encountered in the project is a symbol of strength, isn't it? If you only use some middleware, it is not enough. It is not different from CRUD.

Don't say much, get up. This is the beginning of Eureka's source code analysis. Later, we will continue to share the articles on source code analysis.

First, the startup entry of Eureka service is here: the contextInitialized method of Eureka bootstrap.java.

Just download the source code directly to the official website. https://github.com/Netflix/eureka

This article has been included in my GitHub: https://github.com/Jackson0714/PassJava-Learning

1, Initialize environment

The startup class is eurekabotstrat.java. Under this path:

\eureka\eureka-core\src\main\java\com\netflix\eureka\EurekaBootStrap.java

Startup sequence diagram:

Startup code:

@Override
public void contextInitialized(ServletContextEvent event) {
    initEurekaEnvironment();
    initEurekaServerContext();
    // Omit non core code
}

The method initEurekaEnvironment() for initializing the environment. Click to see what this method does.

String dataCenter = ConfigurationManager.getConfigInstance().getString(EUREKA_DATACENTER);

This is a single instance of the configuration management class. The implementation method of single example uses double detection + volatile

public static AbstractConfiguration getConfigInstance() {
        if (instance == null) {
            synchronized (ConfigurationManager.class) {
                if (instance == null) {
                    instance = getConfigInstance(false));
                }
            }
        }
        return instance;
    }

The instance variable is defined as volatile to ensure visibility.

static volatile AbstractConfiguration instance = null;

After modification, thread A will brush the value of the variable into the main memory, and thread B will brush the value in the main memory back into its own thread memory, that is, after thread A changes, thread B can see the changed value.

You can refer to the article I wrote before: Counter interviewer - 14 schematics - no longer afraid to be asked volatile

The overall process of startup is shown in the figure below:

2, Initialize context

The sequence diagram of initialization context is as follows:

In the contextInitialized method of eurekabeotstrap.java class, the second step calls the initEurekaServerContext() method.

The main operations in initEurekaServerContext are divided into six steps:

The first step is to load the configuration file.

2.1 loading Eureka server configuration file

Obtain the configuration item based on the interface.

The initEurekaServerContext method creates an eurekaServerConfig object:

EurekaServerConfig eurekaServerConfig = new DefaultEurekaServerConfig();

EurekaServerConfig is an interface that defines many methods for obtaining configuration items. It is different from defining constants to get configuration items. For example, get AccessId and SecretKey.

String getAWSAccessId();
String getAWSSecretKey();

There is another way to get configuration items: Config.get(Constants.XX_XX). Compared with the interface above:

  • It is easier to get wrong variables by using constants. Because the definitions of constants are capitalized, they are likely to get XX_XY variable, and the interface method is named by hump, which is easier to identify. For similar variables, take a method name with higher identification.
  • Constants are not easily modified. If you modify the constant name, you need to change all the places used in the global search. If you use the interface method, you only need to modify the place where the constant is referenced in the interface method, which is transparent to the place where the interface method is called.

2.1.1 create a default eureka server configuration

new DefaultEurekaServerConfig() will create a default server configuration, and the constructor will call the init method:

public DefaultEurekaServerConfig() {
    init();
}

2.2.2 loading configuration files

private void init() {
    String env = ConfigurationManager.getConfigInstance().getString(
            EUREKA_ENVIRONMENT, TEST);
    ConfigurationManager.getConfigInstance().setProperty(
            ARCHAIUS_DEPLOYMENT_ENVIRONMENT, env);

    String eurekaPropsFile = EUREKA_PROPS_FILE.get();
    try {
        // ConfigurationManager
        // .loadPropertiesFromResources(eurekaPropsFile);
        ConfigurationManager
                .loadCascadedPropertiesFromResources(eurekaPropsFile);
    } catch (IOException e) {
        logger.warn(
                "Cannot find the properties specified : {}. This may be okay if there are other environment "
                        + "specific properties or the configuration is installed with a different mechanism.",
                eurekaPropsFile);
    }
}

The first two lines are to set the environment name, and the following lines are key statements: obtain the configuration file and put it into the configuration manager singleton.

Take a look at EUREKA_PROPS_FILE.get(); What did you do. First, EUREKA_PROPS_FILE is defined as follows:

private static final DynamicStringProperty EUREKA_PROPS_FILE = DynamicPropertyFactory
        .getInstance().getStringProperty("eureka.server.props",
                "eureka-server");

Set the default value Eureka server with the singleton factory DynamicPropertyFactory, and then EUREKA_PROPS_FILE.get() will the default value from the cache.

Then call the loadCascadedPropertiesFromResources method to load the configuration file.

The default configuration file will be spliced first:

String defaultConfigFileName = configName + ".properties";

Then get the configuration item of the default configuration file:

Properties props = getPropertiesFromFile(url);

Then splice the configuration files of the current environment

String envConfigFileName = configName + "-" + environment + ".properties";

Then get the configuration item of the configuration file of the environment and overwrite the previous default configuration item.

props.putAll(envProps);

The putAll method is to put these attributes into a map.

Then, these configuration items are uniformly handed over to the configuration manager for management:

config.loadProperties(props);

This file is actually loaded:

After opening this file, I found that there were several demo configuration items, but they were annotated.

2.1.3 where are the real configuration items?

You can see that eureka-server.properties are empty. Where are the configuration items configured?

As we said before, DefaultEurekaServerConfig implements the EurekaServerConfig interface, as shown below:

public class DefaultEurekaServerConfig implements EurekaServerConfig

Many get methods are defined in the EurekaServerConfig interface, and DefaultEurekaServerConfig implements these get methods. Let's see how to implement them:

@Override
public int getWaitTimeInMsWhenSyncEmpty() {
    return configInstance.getIntProperty(
            namespace + "waitTimeInMsWhenSyncEmpty", (1000 * 60 * 5)).get();
}

All getXX methods like this have a default value. For example, the above is 1000 * 60 * 5, so we can know that the configuration item is defined in the DefaultEurekaServerConfig class.

The configInstance singleton is of type DynamicPropertyFactory. When creating the configInstance singleton, the ConfigurationManager also does some things: put the configuration items in the configuration file into the DynamicPropertyFactory singleton. In this way, the get method in DefaultEurekaServerConfig can obtain the configuration items in the configuration file. The specific code is in the initWithConfigurationSource method in the DynamicPropertyFactory class.

Combined with the above analysis of loading the configuration file, it can be concluded that if there is no configuration in the configuration file, the default value defined by DefaultEurekaServerConfig is used.

2.1.4 loading profile summary

(1) Create a DefaultEurekaServerConfig object and implement the EurekaServerConfig interface. There are many methods to obtain configuration items.

(2) the init method is invoked in the DefaultEurekaServerConfig constructor.

(3) The init method will load the eureka-server.properties configuration file, put all the configuration items in it into a map, and then hand it over to the configuration manager for management.

(4) There are many get methods in the DefaultEurekaServerConfig object. The name of the configuration item is defined through the hard code. When the get method is called, the method of obtaining the configuration item of DynamicPropertyFactory is called. If these configuration items are in the configuration file, the name of the configuration item is used. The configuration items in the configuration file are assigned to the DynamicPropertyFactory through the ConfigurationManager.

(5) When the configuration item is to be obtained, the corresponding get method is called. If the configuration file is not configured, the default value is used.

2.2 construct instance information manager

2.2.1 initialize the service instance configuration instanceConfig

An ApplicationInfoManager object and service configuration manager are created. Application can be understood as an Eureka client, which is registered with Eureka service as an application.

applicationInfoManager = new ApplicationInfoManager(
        instanceConfig, new EurekaConfigBasedInstanceInfoProvider(instanceConfig).get());

When creating this object, instanceConfig is passed, which is the configuration of eureka instance. This instanceConfig is very similar to EurekaServerConfig mentioned earlier. It implements an interface and obtains configuration information through the getXX method of the interface.

2.2.2 construct service instance instanceInfo

Another parameter is EurekaConfigBasedInstanceInfoProvider, which is used to construct instanceInfo (service instance).

How was it constructed? The constructor pattern in the design pattern is used, and the configuration information is obtained from Eureka instanceconfig.

InstanceInfo.Builder builder = InstanceInfo.Builder.newBuilder(vipAddressResolver);
builder.setXX
    ...
instanceInfo = builder.build();

The code of setXX is as follows:

2.2.3 summary

(1) Initialize the configuration of the service instance instanceConfig.

(2) Initialize the service instance instanceInfo with the constructor pattern.

(3) instanceConfig and instanceInfo are passed to the ApplicationInfoManager for management.

2.3 initialize Eureka client

2.3.1 initialize Eureka client configuration

eureka client is included in eureka server service and is used to communicate with other eureka servers. Why are there other eureka servers? In a cluster environment, there are multiple eureka services, and the services need to communicate with each other.

Initialize Eureka client code:

EurekaClientConfig eurekaClientConfig = new DefaultEurekaClientConfig();
eurekaClient = new DiscoveryClient(applicationInfoManager, eurekaClientConfig);

The first line initializes another configuration, which is very similar to the previous initialization of server config and instance config. The value of the configuration item is also obtained through the DynamicPropertyFactory in the interface method.

Eureka client also has a method to load configuration files:

Archaius1Utils.initConfig(CommonConstants.CONFIG_FILE_NAME);

This file is eureka-client.properties.

When initializing the configuration, a defaulteureka transportconfig () is also initialized, which can be understood as the configuration of the transport.

2.3.2 initialize eurekaClient

Let's look at the second line of code, which creates a DiscoveryClient object and assigns it to eurekaClient.

The process of creating the DiscoveryClient object is very complex. Let's take a closer look.

(1) Get the Eureka client config, transport config, and instance information.

(2) Judge whether to obtain registry information. It will be obtained by default.

if (config.shouldFetchRegistry())

If fetch registry: false is defined in the configuration file, it will not be obtained. In the case of single eureka, the configuration is false because it contains the registry information and does not need to obtain the configuration information from other eureka instances. In the cluster environment, the registry information needs to be obtained.

(3) Judge whether you want to register yourself with other eureka. It will register by default.

if (config.shouldRegisterWithEureka())

In the case of a single machine, configure register with Eureka: false.

(4) A thread pool supporting task scheduling is created.

scheduler = Executors.newScheduledThreadPool(2,
        new ThreadFactoryBuilder()
                .setNameFormat("DiscoveryClient-%d")
                .setDaemon(true)
                .build());

(5) A thread pool supporting heartbeat detection is created.

heartbeatExecutor = new ThreadPoolExecutor(
        1, clientConfig.getHeartbeatExecutorThreadPoolSize(), 0, TimeUnit.SECONDS,
        new SynchronousQueue<Runnable>(),
        new ThreadFactoryBuilder()
                .setNameFormat("DiscoveryClient-HeartbeatExecutor-%d")
                .setDaemon(true)
                .build()
);  // use direct handoff

(6) Created a thread pool that supports cache refresh.

cacheRefreshExecutor = new ThreadPoolExecutor(
        1, clientConfig.getCacheRefreshExecutorThreadPoolSize(), 0, TimeUnit.SECONDS,
        new SynchronousQueue<Runnable>(),
        new ThreadFactoryBuilder()
                .setNameFormat("DiscoveryClient-CacheRefreshExecutor-%d")
                .setDaemon(true)
                .build()
);  // use direct handoff

(7) Created an object that supports communication between eureka client and eureka server

eurekaTransport = new EurekaTransport();

(8) Initialize scheduling task

initScheduledTasks();

In this, it will judge whether it is necessary to start scheduling and refresh the registry information according to the fetch registry. It is scheduled once every 30 s by default. The refresh operation is performed by a CacheRefreshThread thread thread.

Similarly, it will also judge whether it is necessary to start scheduling and execute sending heartbeat according to register with Eureka. It is scheduled once every 30 s by default. The operation of sending heartbeat is executed by a HeartbeatThread thread.

Then it also creates a copy of the instance information to transfer its local instanceInfo instance information to other services. When will this information be sent?

Another listener, statusChangeListener, is created. When the listener listens to the state change, it calls the onDemandUpdate() method of the replica to pass instanceInfo to other services.

2.4 process related to registration

2.4.1 registered object

A PeerAwareInstanceRegistryImpl object is created. You can know by name that it is an implementation class that can sense the cluster instance registry. You can know the function of this class through official comments:

  • Handle all copy operations to other nodes and keep them synchronized. Replication operations include registration, renewal, removal, expiration and status change.

  • When the eureka server starts, it tries to get all the registration information from the cluster node. If the acquisition fails, the current eureka server will not let other applications obtain registration information for a period of time. The default is 5 minutes.

  • Self protection mechanism: if the proportion of application lost and renewed exceeds the set percentage within a certain period of time, eureka will alarm and stop executing expired applications.

registry = new PeerAwareInstanceRegistryImpl(
        eurekaServerConfig,
        eurekaClient.getEurekaClientConfig(),
        serverCodecs,
        eurekaClient
);

PeerAwareInstanceRegistryImpl inherits the AbstractInstanceRegistry abstract class. The constructor mainly does the following:

  • Initialize the configuration information of server config and client config.
this.serverConfig = serverConfig;
this.clientConfig = clientConfig;
  • Initialize the removed queue with a queue length of 1000.
this.recentCanceledQueue = new CircularQueue<Pair<Long, String>>(1000);
  • Initialize the registered queue.
this.recentRegisteredQueue = new CircularQueue<Pair<Long, String>>(1000);

2.5 initialization context

2.5.1 cluster node help class

A peereeurekanodes is created, which is a help class to manage the life cycle of cluster nodes.

PeerEurekaNodes peerEurekaNodes = getPeerEurekaNodes(
        registry,
        eurekaServerConfig,
        eurekaClient.getEurekaClientConfig(),
        serverCodecs,
        applicationInfoManager
);

2.5.2 default context

Created a DefaultEurekaServerContext default context.

serverContext = new DefaultEurekaServerContext(
        eurekaServerConfig,
        serverCodecs,
        registry,
        peerEurekaNodes,
        applicationInfoManager
);

2.5.3 create context holder

A holder is created to hold the context. If you want to get the context elsewhere, you can get it through the holder. Singleton mode is used.

EurekaServerContextHolder.initialize(serverContext);

The initialize() initialization method of holder is a thread safe method.

public static synchronized void initialize(EurekaServerContext serverContext) {
    holder = new EurekaServerContextHolder(serverContext);
}

Defines a static private holder variable

private static EurekaServerContextHolder holder;

If you want to get the holder elsewhere, you can get the holder through the getInstance() method.

public static EurekaServerContextHolder getInstance() {
    return holder;
}

Then, if you want to get the context, call the holder's getServerContext() method.

public EurekaServerContext getServerContext() {
    return this.serverContext;
}

2.5.4 initialization context

Call the initialize() method of serverContext to initialize.

public void initialize() throws Exception {
    logger.info("Initializing ...");
    peerEurekaNodes.start();
    registry.init(peerEurekaNodes);
    logger.info("Initialized");
}

peerEurekaNodes.start();

A scheduled task is started to put the url of the cluster node into the collection, which does not contain the url of the local node. Update the information of eureka server cluster at regular intervals.

registry.init(peerEurekaNodes);

The registry will be initialized, the registration information in the cluster will be obtained, and then put into the registry.

2.6 others

2.6.1 copy registration information from adjacent nodes

int registryCount = registry.syncUp();

2.6.2 eureka monitoring

EurekaMonitors.registerAllStats();

2.7 solution to compilation error

1. Exception 1

An exception occurred applying plugin request [id: 'nebula.netflixoss', version: '3.6.0']

Solution

plugins {
  id 'nebula.netflixoss' version '5.1.1'
}

Exception 2

eureka-server-governator Plugin with id 'jetty' not found.

reference resources https://blog.csdn.net/Sino_Crazy_Snail/article/details/79300058

3, Summary

Let's have an overall flow chart of Eureka startup

Posted by ababmxking on Mon, 18 Oct 2021 10:34:34 -0700