spring-cloud-eureka (1) principle analysis

spring-cloud-eureka is part of the spring-cloud-nettfix microservice suite. It is encapsulated twice based on nettfix-eureka and is mainly responsible for the service governance function in the microservice architecture.
Friends who know dubbo should know that dubbo is a service governance framework, and dubbo implements service governance functions based on zookeeper. As for the difference between dubbo and nettfix-eureka, I won't say much here. There are many such articles on the Internet.

Service governance generally has two functions: service registration and service discovery. There is usually a registration center, and each service unit registers its own information with the registration center, such as the services provided, ip, port, and some nearby information. The registry will send the new service instance to other instances that depend on the service.
Service Governance Mechanism

service registration

The service provider will register its own information with the Eureka Server when it starts up. After the Eureka Server receives the information, it will store the data information in a double-layered Map, where the key of the first layer is the service name, and the second layer The key is the instance name of the specific service.

service synchronization

If there are multiple Eureka Servers and a service provider is registered with one of the Eureka Servers, the Eureka Server will forward the registration information of the service provider to other Eureka Servers in the cluster, thereby realizing service synchronization between Eureka Servers.

Service renewal

After the registration is completed, the service provider will maintain a mind and continuously send information to the Eureka Server (registration center) to indicate normal operation, so as to prevent the Eureka Server from removing the service instance from the service list.

service offline

When the service instance is shut down normally, it will send a service offline message to the registry. After the registry receives the information, it will set the service instance status as offline and spread the information.

Get service

When a service instance depends on another service, the service instance acts as a service consumer, and it will send a message to the registry to request a list of registered services, and the registry will maintain a read-only service list to Returned to the service consumer.

fail rejection

Sometimes, the service instance may not be able to provide services normally, and the registry does not receive the information that the service is offline. The registry will create a scheduled task to remove service instances that have no service renewal message for a certain period of time from the service list.

self protection

As mentioned above, when failover is mentioned, the instances that have not received the service renewal message for more than a certain period of time will be eliminated from the service list, and there is a logic in the middle. If the percentage of successful heartbeat statistics during operation is lower than 85% (heartbeat threshold), the registry will protect the instance registration information in the current service list so that these instances will not expire. However, in this case, if there is a problem with the service instance, the service consumer may get the service instance that is actually not running normally, and the call will fail. Therefore, the client needs to have a fault tolerance mechanism, such as requesting retry. , or circuit breakers, etc.

However, there is a scheduled task that is executed every 15 minutes by default, and the heartbeat threshold will be recalculated according to the operating status; but it may not be recalculated. At this time, the self-protection state of Eureka Server will always exist.

If you want to turn off the self-protection mechanism, you can set eureka.server.enable-self-preservation to false to ensure that the registry removes unavailable service instances in time.


Source code analysis

Eureka is divided into a registration center, that is, Eureka-Server, or a server. The service instance registered to the server is called a client, and the client is abstracted into a service provider and a service consumer. Among them, in Eureka, a client is both a service provider and a service consumer. The server will also register its own information with another server instance to implement a Server cluster.


Eureka Client startup process

The first is to start the service, you need to add the @EnableDiscoveryClient annotation, so that the Eureka-related code will be loaded and run at startup. Check the source code of @EnableDiscoveryClient, where @Import(EnableDiscoveryClientImportSelector.class) points to the org.springframework.cloud.client.discovery.EnableDiscoveryClientImportSelector class, which inherits org.springframework.cloud.commons.util.SpringFactoryImportSelector, so it will execute spring. The key in factories is the @Configuration class of org.springframework.cloud.client.discovery.EnableDiscoveryClient

The code example is as follows:

package org.springframework.cloud.client.discovery;

...

/**
 * @author Spencer Gibb
 */
@Order(Ordered.LOWEST_PRECEDENCE - 100)
public class EnableDiscoveryClientImportSelector
        extends SpringFactoryImportSelector<EnableDiscoveryClient> {
        ... 
}

spring-cloud-netflix-eureka-client-{version}.jar包下spring.factories

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.cloud.netflix.eureka.config.EurekaClientConfigServerAutoConfiguration,\
org.springframework.cloud.netflix.eureka.config.EurekaDiscoveryClientConfigServiceAutoConfiguration,\
org.springframework.cloud.netflix.eureka.EurekaClientAutoConfiguration,\
org.springframework.cloud.netflix.ribbon.eureka.RibbonEurekaAutoConfiguration

org.springframework.cloud.bootstrap.BootstrapConfiguration=\
org.springframework.cloud.netflix.eureka.config.EurekaDiscoveryClientConfigServiceBootstrapConfiguration

org.springframework.cloud.client.discovery.EnableDiscoveryClient=\
org.springframework.cloud.netflix.eureka.EurekaDiscoveryClientConfiguration

It can be seen that the EurekaDiscoveryClientConfiguration configuration class will be executed first, the configuration in it will be loaded, and then the EurekaClientAutoConfiguration configuration class will be executed. The code for the two configuration classes is as follows:

EurekaDiscoveryClientConfiguration

/*
 * Copyright 2013-2014 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.cloud.netflix.eureka;

...
import org.springframework.boot.actuate.health.HealthAggregator;
import org.springframework.boot.actuate.health.OrderedHealthAggregator;
...
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.cloud.context.scope.refresh.RefreshScopeRefreshedEvent;
import org.springframework.cloud.netflix.eureka.serviceregistry.EurekaAutoServiceRegistration;
...
import org.springframework.context.event.EventListener;

import com.netflix.appinfo.EurekaInstanceConfig;
import com.netflix.appinfo.HealthCheckHandler;
import com.netflix.discovery.EurekaClient;
import com.netflix.discovery.EurekaClientConfig;

import lombok.extern.apachecommons.CommonsLog;

/**
 * @author Dave Syer
 * @author Spencer Gibb
 * @author Jon Schneider
 * @author Jakub Narloch
 */
@Configuration
@EnableConfigurationProperties
@ConditionalOnClass(EurekaClientConfig.class)
@ConditionalOnProperty(value = "eureka.client.enabled", matchIfMissing = true)
@CommonsLog
public class EurekaDiscoveryClientConfiguration {

    class Marker {}

    //Eureka discover client(eureka 客户端) 标记
    @Bean
    public Marker eurekaDiscoverClientMarker() {
        return new Marker();
    }

    @Configuration
    @ConditionalOnClass(RefreshScopeRefreshedEvent.class)
    protected static class EurekaClientConfigurationRefresher {

        @Autowired(required = false)
        private EurekaAutoServiceRegistration autoRegistration;

        @EventListener(RefreshScopeRefreshedEvent.class)
        public void onApplicationEvent(RefreshScopeRefreshedEvent event) {
            if (autoRegistration != null) {
                // register in case meta data changed
                this.autoRegistration.stop();
                this.autoRegistration.start();
            }
        }
    }

    @Configuration
    @ConditionalOnClass(Endpoint.class)
    protected static class EurekaHealthIndicatorConfiguration {
        @Bean
        @ConditionalOnMissingBean
        public EurekaHealthIndicator eurekaHealthIndicator(EurekaClient eurekaClient,
                EurekaInstanceConfig instanceConfig, EurekaClientConfig clientConfig) {
            return new EurekaHealthIndicator(eurekaClient, instanceConfig, clientConfig);
        }
    }

    @Configuration
    @ConditionalOnProperty(value = "eureka.client.healthcheck.enabled", matchIfMissing = false)
    protected static class EurekaHealthCheckHandlerConfiguration {

        @Autowired(required = false)
        private HealthAggregator healthAggregator = new OrderedHealthAggregator();

        @Bean
        @ConditionalOnMissingBean(HealthCheckHandler.class)
        public EurekaHealthCheckHandler eurekaHealthCheckHandler() {
            return new EurekaHealthCheckHandler(this.healthAggregator);
        }
    }
}

EurekaClientAutoConfiguration

package org.springframework.cloud.netflix.eureka;

...

/**
 * @author Dave Syer
 * @author Spencer Gibb
 * @author Jon Schneider
 * @author Matt Jenkins
 * @author Ryan Baxter
 */
@Configuration
@EnableConfigurationProperties
@ConditionalOnClass(EurekaClientConfig.class)
//在EurekaDiscoveryClientConfiguration类中创建发布的标记类
@ConditionalOnBean(EurekaDiscoveryClientConfiguration.Marker.class)
@ConditionalOnProperty(value = "eureka.client.enabled", matchIfMissing = true)
@AutoConfigureBefore({ NoopDiscoveryClientAutoConfiguration.class,
        CommonsClientAutoConfiguration.class, ServiceRegistryAutoConfiguration.class })
@AutoConfigureAfter(name = "org.springframework.cloud.autoconfigure.RefreshAutoConfiguration")
public class EurekaClientAutoConfiguration {
    ...
    /**
     *在spring-cloud-context-{version}.jar!META-INF/spring.factories中有一段:
     org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.cloud.autoconfigure.ConfigurationPropertiesRebinderAutoConfiguration,\
org.springframework.cloud.autoconfigure.RefreshAutoConfiguration,\
org.springframework.cloud.autoconfigure.RefreshEndpointAutoConfiguration,\
org.springframework.cloud.autoconfigure.LifecycleMvcEndpointAutoConfiguration

其中包含了RefreshAutoConfiguration配置类,所以会通过下面这个配置类创建EurekaClient
*/
    @Configuration
    @ConditionalOnRefreshScope
    protected static class RefreshableEurekaClientConfiguration {

        @Autowired
        private ApplicationContext context;

        @Autowired(required = false)
        private DiscoveryClientOptionalArgs optionalArgs;



        //创建EurekaClient
        @Bean(destroyMethod = "shutdown")
        @ConditionalOnMissingBean(value = EurekaClient.class, search = SearchStrategy.CURRENT)
        @org.springframework.cloud.context.config.annotation.RefreshScope
        @Lazy
        public EurekaClient eurekaClient(ApplicationInfoManager manager,
                EurekaClientConfig config, EurekaInstanceConfig instance) {
            manager.getInfo(); // force initialization
            return new CloudEurekaClient(manager, config, this.optionalArgs,
                    this.context);
        }

        @Bean
        @ConditionalOnMissingBean(value = ApplicationInfoManager.class, search = SearchStrategy.CURRENT)
        @org.springframework.cloud.context.config.annotation.RefreshScope
        @Lazy
        public ApplicationInfoManager eurekaApplicationInfoManager(
                EurekaInstanceConfig config) {
            InstanceInfo instanceInfo = new InstanceInfoFactory().create(config);
            return new ApplicationInfoManager(config, instanceInfo);
        }

    }
    ...
}

The above is the startup logic of @EnableDiscoveryClient. From the comments of EnableDiscoveryClient, we can see that it is mainly used to start the instance of DiscoveryClient. Searching for DiscoveryClient found that there is a class and an interface, of which org.springframework.cloud.client.discovery.DiscoveryClient is the interface of spring cloud, which defines the common abstract method of discovery service, through which the implementation details of service governance can be effectively shielded , so spring cloud can easily switch between different service governance frameworks when building microservice applications. org.springframework.cloud.netflix.eureka.EurekaDiscoveryClient is the implementation of this interface. It encapsulates the Eureka service discovery and relies on the com.netflix.discovery.EurekaClient interface of Netfix Eureka. The EurekaClient interface inherits the LookupService interface. These two The interface is the content in the Netflix Eureka open source package, which defines the abstract method of Eureka service discovery, and its implementation class is the com.netflix.discovery.DiscoveryClient class.

It can be seen from the comments of this class that this class includes functions such as service registration, service renewal, service offline, and service acquisition.

According to the above code, you can know that EurekaClient is created in the EurekaClientAutoConfiguration$RefreshableEurekaClientConfiguration#eurekaClient(ApplicationInfoManager, EurekaClientConfig, EurekaInstanceConfig) method, which is org.springframework.cloud.netflix.eureka.CloudEurekaClient.

EurekaClientAutoConfiguration$RefreshableEurekaClientConfiguration

    @Configuration
    @ConditionalOnRefreshScope
    protected static class RefreshableEurekaClientConfiguration {

        @Autowired
        private ApplicationContext context;

        @Autowired(required = false)
        private DiscoveryClientOptionalArgs optionalArgs;

        @Bean(destroyMethod = "shutdown")
        @ConditionalOnMissingBean(value = EurekaClient.class, search = SearchStrategy.CURRENT)
        @org.springframework.cloud.context.config.annotation.RefreshScope
        @Lazy
        public EurekaClient eurekaClient(ApplicationInfoManager manager,
                EurekaClientConfig config, EurekaInstanceConfig instance) {
            manager.getInfo(); // force initialization
            return new CloudEurekaClient(manager, config, this.optionalArgs,
                    this.context);
        }
        ...
    }

CloudEurekaClient

package org.springframework.cloud.netflix.eureka;

...

public class CloudEurekaClient extends DiscoveryClient {
    ...
    public CloudEurekaClient(ApplicationInfoManager applicationInfoManager,
                             EurekaClientConfig config,
                             DiscoveryClientOptionalArgs args,
                             ApplicationEventPublisher publisher) {
        super(applicationInfoManager, config, args);
        this.applicationInfoManager = applicationInfoManager;
        this.publisher = publisher;
        this.eurekaTransportField = ReflectionUtils.findField(DiscoveryClient.class, "eurekaTransport");
        ReflectionUtils.makeAccessible(this.eurekaTransportField);
    }
    ...
}

It can be seen that CloudEurekaClient inherits com.netflix.discovery.DiscoveryClient, and calls the initScheduledTasks() method in the construction method. In this method, according to the eureka.client.fetch-registry and eureka.client.register-with- The value of eureka determines whether to create a timed task for pulling the service list, a timed task for service renewal, and a timed task for service registration.

CloudEurekaClient

...
public class DiscoveryClient implements EurekaClient {
    ...
    /**
     * Initializes all scheduled tasks.
     */
    private void initScheduledTasks() {
        if (clientConfig.shouldFetchRegistry()) {
            // registry cache refresh timer
            int registryFetchIntervalSeconds = clientConfig.getRegistryFetchIntervalSeconds();
            int expBackOffBound = clientConfig.getCacheRefreshExecutorExponentialBackOffBound();
            //创建拉取服务清单的定时任务
            scheduler.schedule(
                    new TimedSupervisorTask(
                            "cacheRefresh",
                            scheduler,
                            cacheRefreshExecutor,
                            registryFetchIntervalSeconds,
                            TimeUnit.SECONDS,
                            expBackOffBound,
                            new CacheRefreshThread()
                    ),
                    registryFetchIntervalSeconds, TimeUnit.SECONDS);
        }

        if (clientConfig.shouldRegisterWithEureka()) {
            int renewalIntervalInSecs = instanceInfo.getLeaseInfo().getRenewalIntervalInSecs();
            int expBackOffBound = clientConfig.getHeartbeatExecutorExponentialBackOffBound();
            logger.info("Starting heartbeat executor: " + "renew interval is: " + renewalIntervalInSecs);

            //创建服务续约的定时任务(心跳)
            // Heartbeat timer
            scheduler.schedule(
                    new TimedSupervisorTask(
                            "heartbeat",
                            scheduler,
                            heartbeatExecutor,
                            renewalIntervalInSecs,
                            TimeUnit.SECONDS,
                            expBackOffBound,
                            new HeartbeatThread()
                    ),
                    renewalIntervalInSecs, TimeUnit.SECONDS);

            //这是一个Runnable接口实现类,服务注册的逻辑就在run()方法中。
            // InstanceInfo replicator
            instanceInfoReplicator = new InstanceInfoReplicator(
                    this,
                    instanceInfo,
                    clientConfig.getInstanceInfoReplicationIntervalSeconds(),
                    2); // burstSize
            //服务实例状态监听器
            statusChangeListener = new ApplicationInfoManager.StatusChangeListener() {
                @Override
                public String getId() {
                    return "statusChangeListener";
                }

                @Override
                public void notify(StatusChangeEvent statusChangeEvent) {
                    if (InstanceStatus.DOWN == statusChangeEvent.getStatus() ||
                            InstanceStatus.DOWN == statusChangeEvent.getPreviousStatus()) {
                        // log at warn level if DOWN was involved
                        logger.warn("Saw local status change event {}", statusChangeEvent);
                    } else {
                        logger.info("Saw local status change event {}", statusChangeEvent);
                    }
//如果状态发生改变, 重新将实例信息注册到注册中心
instanceInfoReplicator.onDemandUpdate();
                }
            };
        //判断配置中是否设置了注册服务实例状态监听器
            if (clientConfig.shouldOnDemandUpdateStatusChange()) {
                applicationInfoManager.registerStatusChangeListener(statusChangeListener);
            }

//将自身注册到定时任务中
instanceInfoReplicator.start(clientConfig.getInitialInstanceInfoReplicationIntervalSeconds());
        } else {
            logger.info("Not registering with Eureka server per configuration");
        }
    }
    ...

}

InstanceInfoReplicator

class InstanceInfoReplicator implements Runnable {
    ...
    public void start(int initialDelayMs) {
        if (started.compareAndSet(false, true)) {
            instanceInfo.setIsDirty();  // for initial register
            //将注册的动作添加到定时任务中
            Future next = scheduler.schedule(this, initialDelayMs, TimeUnit.SECONDS);
            scheduledPeriodicRef.set(next);
        }
    }

    public void stop() {
        scheduler.shutdownNow();
        started.set(false);
    }

    public boolean onDemandUpdate() {
        if (rateLimiter.acquire(burstSize, allowedRatePerMinute)) {
            scheduler.submit(new Runnable() {
                @Override
                public void run() {
                    logger.debug("Executing on-demand update of local InstanceInfo");

                    Future latestPeriodic = scheduledPeriodicRef.get();
                    if (latestPeriodic != null && !latestPeriodic.isDone()) {
                        logger.debug("Canceling the latest scheduled update, it will be rescheduled at the end of on demand update");
                        latestPeriodic.cancel(false);
                    }

                    InstanceInfoReplicator.this.run();
                }
            });
            return true;
        } else {
            logger.warn("Ignoring onDemand update due to rate limiter");
            return false;
        }
    }

    public void run() {
        try {
            discoveryClient.refreshInstanceInfo();

            Long dirtyTimestamp = instanceInfo.isDirtyWithTime();
            if (dirtyTimestamp != null) {
                //注册到注册中心,并标记
                discoveryClient.register();
                instanceInfo.unsetIsDirty(dirtyTimestamp);
            }
        } catch (Throwable t) {
            logger.warn("There was a problem with the instance info replicator", t);
        } finally {
            Future next = scheduler.schedule(this, replicationIntervalSeconds, TimeUnit.SECONDS);
            scheduledPeriodicRef.set(next);
        }
    }
}

As can be seen from the above code, when the service starts, related timing tasks are created according to the configuration, such as service registration, service acquisition, service renewal, but the logical entry of this function is still in com.netflix.discovery. In DiscoveryClient:

package com.netflix.discovery;
...
@Singleton
public class DiscoveryClient implements EurekaClient {
    ...
    /**
     * Register with the eureka service by making the appropriate REST call.
     */
     //注册
    boolean register() throws Throwable {
        logger.info(PREFIX + appPathIdentifier + ": registering service...");
        EurekaHttpResponse<Void> httpResponse;
        try {
            httpResponse = eurekaTransport.registrationClient.register(instanceInfo);
        } catch (Exception e) {
            logger.warn("{} - registration failed {}", PREFIX + appPathIdentifier, e.getMessage(), e);
            throw e;
        }
        if (logger.isInfoEnabled()) {
            logger.info("{} - registration status: {}", PREFIX + appPathIdentifier, httpResponse.getStatusCode());
        }
        return httpResponse.getStatusCode() == 204;
    }

    /**
     * Renew with the eureka service by making the appropriate REST call
     */
     //续约
    boolean renew() {
        EurekaHttpResponse<InstanceInfo> httpResponse;
        try {
            httpResponse = eurekaTransport.registrationClient.sendHeartBeat(instanceInfo.getAppName(), instanceInfo.getId(), instanceInfo, null);
            logger.debug("{} - Heartbeat status: {}", PREFIX + appPathIdentifier, httpResponse.getStatusCode());
            if (httpResponse.getStatusCode() == 404) {
                REREGISTER_COUNTER.increment();
                logger.info("{} - Re-registering apps/{}", PREFIX + appPathIdentifier, instanceInfo.getAppName());
                return register();
            }
            return httpResponse.getStatusCode() == 200;
        } catch (Throwable e) {
            logger.error("{} - was unable to send heartbeat!", PREFIX + appPathIdentifier, e);
            return false;
        }
    }
    ...
    //下线
    @PreDestroy
    @Override
    public synchronized void shutdown() {
        if (isShutdown.compareAndSet(false, true)) {
            logger.info("Shutting down DiscoveryClient ...");

            if (statusChangeListener != null && applicationInfoManager != null) {
                applicationInfoManager.unregisterStatusChangeListener(statusChangeListener.getId());
            }

            cancelScheduledTasks();

            // If APPINFO was registered
            if (applicationInfoManager != null && clientConfig.shouldRegisterWithEureka()) {
                applicationInfoManager.setInstanceStatus(InstanceStatus.DOWN);
                unregister();
            }

            if (eurekaTransport != null) {
                eurekaTransport.shutdown();
            }

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

            logger.info("Completed shut down of DiscoveryClient");
        }
    }
    ...
    //获取服务
    private boolean fetchRegistry(boolean forceFullRegistryFetch) {
        Stopwatch tracer = FETCH_REGISTRY_TIMER.start();

        try {
            // If the delta is disabled or if it is the first time, get all
            // applications
            Applications 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 delta
            {
                logger.info("Disable delta property : {}", clientConfig.shouldDisableDelta());
                logger.info("Single vip registry refresh property : {}", clientConfig.getRegistryRefreshSingleVipAddress());
                logger.info("Force full registry fetch : {}", forceFullRegistryFetch);
                logger.info("Application is null : {}", (applications == null));
                logger.info("Registered Applications size is zero : {}",
                        (applications.getRegisteredApplications().size() == 0));
                logger.info("Application version is -1: {}", (applications.getVersion() == -1));
                getAndStoreFullRegistry();
            } else {
                getAndUpdateDelta(applications);
            }
            applications.setAppsHashCode(applications.getReconcileHashCode());
            logTotalInstances();
        } catch (Throwable e) {
            logger.error(PREFIX + appPathIdentifier + " - was unable to refresh its cache! status = " + e.getMessage(), e);
            return false;
        } finally {
            if (tracer != null) {
                tracer.stop();
            }
        }

        // Notify about cache refresh before updating the instance remote status
        onCacheRefreshed();

        // Update remote status based on refreshed data held in the cache
        updateInstanceRemoteStatus();

        // registry was fetched successfully, so return true
        return true;
    }
    ...

    void refreshRegistry() {
        try {
            ...

            boolean success = fetchRegistry(remoteRegionsModified);
            if (success) {
                registrySize = localRegionApps.get().size();
                lastSuccessfulRegistryFetchTimestamp = System.currentTimeMillis();
            }

            ...       
    }
    ...
}

Eureka Server startup process

Like Eureka Client startup, @EnableEurekaServer annotation needs to be added. In this class, @Import(EurekaServerMarkerConfiguration.class) is used to indicate that the program will first load the configuration in the EurekaServerMarkerConfiguration configuration class at startup, and in this configuration class, a marker class EurekaServerMarkerConfiguration$Marker is published, which will be used for Start the loading of subsequent EurekaServer related configurations.

org.springframework.cloud.netflix.eureka.server.EnableEurekaServer

package org.springframework.cloud.netflix.eureka.server;
...
@EnableDiscoveryClient
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(EurekaServerMarkerConfiguration.class)
public @interface EnableEurekaServer {

}

org.springframework.cloud.netflix.eureka.server.EurekaServerMarkerConfiguration

package org.springframework.cloud.netflix.eureka.server;
...
@Configuration
public class EurekaServerMarkerConfiguration {

    @Bean
    public Marker eurekaServerMarkerBean() {
        return new Marker();
    }

    class Marker {
    }
}

spring-cloud-netflix-eureka-server-{version}.jar!META-INF/spring.factories

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
  org.springframework.cloud.netflix.eureka.server.EurekaServerAutoConfiguration

From the above configuration, find the EurekaServerAutoConfiguration configuration class that will be automatically loaded

org.springframework.cloud.netflix.eureka.server.EurekaServerAutoConfiguration

package org.springframework.cloud.netflix.eureka.server;
...
@Configuration
@Import(EurekaServerInitializerConfiguration.class)
@ConditionalOnBean(EurekaServerMarkerConfiguration.Marker.class)
@EnableConfigurationProperties({ EurekaDashboardProperties.class,
        InstanceRegistryProperties.class })
@PropertySource("classpath:/eureka/server.properties")
public class EurekaServerAutoConfiguration extends WebMvcConfigurerAdapter {
    ...
    private static String[] EUREKA_PACKAGES = new String[] { "com.netflix.discovery",
            "com.netflix.eureka" };
    ...

    /**
     * Register the Jersey filter
     */
    @Bean
    public FilterRegistrationBean jerseyFilterRegistration(
            javax.ws.rs.core.Application eurekaJerseyApp) {
        FilterRegistrationBean bean = new FilterRegistrationBean();
        bean.setFilter(new ServletContainer(eurekaJerseyApp));
        bean.setOrder(Ordered.LOWEST_PRECEDENCE);
        //(2)创建Filter,并匹配路径/eureka/*
        bean.setUrlPatterns(
                Collections.singletonList(EurekaConstants.DEFAULT_PREFIX + "/*"));

        return bean;
    }

    /**
     * Construct a Jersey {@link javax.ws.rs.core.Application} with all the resources
     * required by the Eureka server.
     */
    @Bean
    public javax.ws.rs.core.Application jerseyApplication(Environment environment,
            ResourceLoader resourceLoader) {

        ClassPathScanningCandidateComponentProvider provider = new ClassPathScanningCandidateComponentProvider(
                false, environment);

        // Filter to include only classes that have a particular annotation.
        //
        // (1) 创建相关的web节点, 比如注册接口/eureka/apps/{appId}
        provider.addIncludeFilter(new AnnotationTypeFilter(Path.class));
        provider.addIncludeFilter(new AnnotationTypeFilter(Provider.class));

        // Find classes in Eureka packages (or subpackages)
        //
        Set<Class<?>> classes = new HashSet<Class<?>>();
        //扫描restful接口资源的类
        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);
            }
        }

        // Construct the Jersey ResourceConfig
        //
        Map<String, Object> propsAndFeatures = new HashMap<String, Object>();
        propsAndFeatures.put(
                // Skip static content used by the webapp
                ServletContainer.PROPERTY_WEB_PAGE_CONTENT_REGEX,
                EurekaConstants.DEFAULT_PREFIX + "/(fonts|images|css|js)/.*");

        DefaultResourceConfig rc = new DefaultResourceConfig(classes);
        rc.setPropertiesAndFeatures(propsAndFeatures);

        return rc;
    }

    ...
}

The above code mainly introduces the process of creating related interfaces when Eureka Server starts up. These interfaces are used for service registration, service renewal, and service offline. These interfaces are restful interfaces published by jersey, and the resource classes are all under the com.netflix.eureka.resources package.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=326487169&siteId=291194637