Eureka architecture diagram

Eureka as the registry of springCloud, provide service registration, service renewal, service synchronization and other functions, this article combines the source code to see Eureka core functions, original address

Eureka core functions

  • Service Register: The Eureka Client registers its services with the Eureka Server by sending REST requests and provides its own metadata, such as IP address, port, URL of health indicator, and home page address. When the Eureka Server receives the registration request, it stores the metadata information in a two-tier Map.
  • Renew: After the service is registered, the Eureka Client maintains a heartbeat to continuously notify the Eureka Server that the service is available all the time to prevent it from being deleted. Eureka Client will every 30 seconds by default (Eureka. Instance. LeaseRenewallIntervalInSeconds) sends a heartbeat to service contract.
  • Service Replication (REPLICATE) : Eureka servers register with each other to build a Eureka Server cluster. Services between different Eureka servers are synchronized to ensure service information consistency.
  • Get Registry: When the Eureka Client is started, it sends a REST request to the Eureka Server to retrieve the list of registered services and cache them locally to the Eureka Client. The default cache 30 seconds (eureka. Client. RegistryFetchIntervalSeconds). Also, for performance purposes, the Eureka Server maintains a read-only service clearance cache that is updated every 30 seconds.
  • Service invocation: After obtaining the list of services, the service consumer can find the address of other services according to the list of services information, so as to make a remote invocation. Eureka has the concepts of Region and Zone. A Region can contain multiple zones. Service providers in the same Zone are preferentially accessed during service invocation.
  • Service offline (Cancel) : When the Eureka Client needs to be shut down or restarted, it does not want any more requests to come in during this period. Therefore, it needs to send a REST request to the Eureka Server in advance to inform the Eureka Server that it is going offline. The service state is set to DOWN and the offline event is propagated.
  • Service cull (EVICT) : Sometimes, a service instance cannot provide services due to network faults, and the instance does not send a request to the Eureka Server to take the service offline. Therefore, a service cull mechanism is required. When Eureka Server is started, it creates a scheduled task, and every once in a while (60 seconds by default), it does not renew the timeout from the current service list (90 seconds by default). Eureka. Instance. LeaseExpirationDurationInSeconds) service.
  • Self-protection: Since the Eureka Server periodically deletes the services that are not renewed due to timeout, it is possible that a network exception occurs during a period of time, and all services fail to be renewed. Therefore, the Eureka Server deletes all services. This is obviously unreasonable. Therefore, there is a self-protection mechanism. In a short period of time, the rate of renewal failure is calculated. If the rate reaches a certain threshold, the self-protection mechanism will be triggered. Enable-self-preservation switch (eureca.server.enable-self-preservation: false)

##Eureka Server source analysis

EurekaServerAutoConfiguration

Part of the core code is filtered for explanation

@Configuration( proxyBeanMethods = false )
@Import({EurekaServerInitializerConfiguration.class})
@ConditionalOnBean({Marker.class})
@EnableConfigurationProperties({EurekaDashboardProperties.class, InstanceRegistryProperties.class})
@PropertySource({"classpath:/eureka/server.properties"})
public class EurekaServerAutoConfiguration implements WebMvcConfigurer {
    private static final String[] EUREKA_PACKAGES = new String[]{"com.netflix.discovery"."com.netflix.eureka"};
    @Autowired
    private ApplicationInfoManager applicationInfoManager;
    @Autowired
    private EurekaServerConfig eurekaServerConfig;
    @Autowired
    private EurekaClientConfig eurekaClientConfig;
    @Autowired
    private EurekaClient eurekaClient;
    @Autowired
    private InstanceRegistryProperties instanceRegistryProperties;
    public static final CloudJacksonJson JACKSON_JSON = new CloudJacksonJson();

    public EurekaServerAutoConfiguration(a) {}@Bean
    public HasFeatures eurekaServerFeature(a) {
        return HasFeatures.namedFeature("Eureka Server", EurekaServerAutoConfiguration.class);
    }

  // Load EurekaController. Spring ‐ Cloud provides additional interfaces for retrieving information about eurekaServer
    @Bean
    @ConditionalOnProperty( prefix = "eureka.dashboard", name = {"enabled"}, matchIfMissing = true )
    public EurekaController eurekaController(a) {
        return new EurekaController(this.applicationInfoManager);
    }

    @Bean
    public ServerCodecs serverCodecs(a) {
        return new EurekaServerAutoConfiguration.CloudServerCodecs(this.eurekaServerConfig);
    }

    private static CodecWrapper getFullJson(EurekaServerConfig serverConfig) {
        CodecWrapper codec = CodecWrappers.getCodec(serverConfig.getJsonCodecName());
        return codec == null ? CodecWrappers.getCodec(JACKSON_JSON.codecName()) : codec;
    }

    private static CodecWrapper getFullXml(EurekaServerConfig serverConfig) {
        CodecWrapper codec = CodecWrappers.getCodec(serverConfig.getXmlCodecName());
        return codec == null ? CodecWrappers.getCodec(XStreamXml.class) : codec;
    }

    @Bean
    @ConditionalOnMissingBean
    public ReplicationClientAdditionalFilters replicationClientAdditionalFilters(a) {
        return new ReplicationClientAdditionalFilters(Collections.emptySet());
    }

  // Initialize the cluster registry
    @Bean
    public PeerAwareInstanceRegistry peerAwareInstanceRegistry(ServerCodecs serverCodecs) {
        this.eurekaClient.getApplications();
        return new InstanceRegistry(this.eurekaServerConfig, this.eurekaClientConfig, serverCodecs, this.eurekaClient, this.instanceRegistryProperties.getExpectedNumberOfClientsSendingRenews(), this.instanceRegistryProperties.getDefaultOpenForTrafficCount());
    }

   // Configure the service node information. This function is used to configure the Eureka peer node
    // We need to notify those service nodes (cluster to each other)
    @Bean
    @ConditionalOnMissingBean
    public PeerEurekaNodes peerEurekaNodes(PeerAwareInstanceRegistry registry, ServerCodecs serverCodecs, ReplicationClientAdditionalFilters replicationClientAdditionalFilters) {
        return new EurekaServerAutoConfiguration.RefreshablePeerEurekaNodes(registry, this.eurekaServerConfig, this.eurekaClientConfig, serverCodecs, this.applicationInfoManager, replicationClientAdditionalFilters);
    }

  / / EurekaServer context
    @Bean
    @ConditionalOnMissingBean
    public EurekaServerContext eurekaServerContext(ServerCodecs serverCodecs, PeerAwareInstanceRegistry registry, PeerEurekaNodes peerEurekaNodes) {
        return new DefaultEurekaServerContext(this.eurekaServerConfig, serverCodecs, registry, peerEurekaNodes, this.applicationInfoManager);
    }

  // This class is used for spring‐cloud and native Eureka glue code to start EurekaSever
  / / this class will be behind EurekaServerInitializerConfiguration is called, had been launched
    @Bean
    public EurekaServerBootstrap eurekaServerBootstrap(PeerAwareInstanceRegistry registry, EurekaServerContext serverContext) {
        return new EurekaServerBootstrap(this.applicationInfoManager, this.eurekaClientConfig, this.eurekaServerConfig, registry, serverContext);
    }

    // Configure the interceptor. ServletContainer implements the Jersey framework to implement the restFull interface of eurekaServer
    @Bean
    publicFilterRegistrationBean<? > jerseyFilterRegistration(Application eurekaJerseyApp) { FilterRegistrationBean<Filter> bean =new FilterRegistrationBean();
        bean.setFilter(new ServletContainer(eurekaJerseyApp));
        bean.setOrder(2147483647);
        bean.setUrlPatterns(Collections.singletonList("/eureka/*"));
        returnbean; }}Copy the code

EurekaServerInitializerConfiguration

Will import EurekaServerInitializerConfiguration EurekaServerAutoConfiguration

@Configuration( proxyBeanMethods = false )
public class EurekaServerInitializerConfiguration implements ServletContextAware.SmartLifecycle.Ordered {
    private static final Log log = LogFactory.getLog(EurekaServerInitializerConfiguration.class);
    @Autowired
    private EurekaServerConfig eurekaServerConfig;
    private ServletContext servletContext;
    @Autowired
    private ApplicationContext applicationContext;
    @Autowired
    private EurekaServerBootstrap eurekaServerBootstrap;
    private boolean running;
    private int order = 1;

    public EurekaServerInitializerConfiguration(a) {}public void setServletContext(ServletContext servletContext) {
        this.servletContext = servletContext;
    }

     // Start a thread
    public void start(a) {(new Thread(() -> {
            try {
                // Initialize the EurekaServer and register the EurekaServer
                this.eurekaServerBootstrap.contextInitialized(this.servletContext);
                log.info("Started Eureka Server");
                // Publish the EurekaServer registration event
                this.publish(new EurekaRegistryAvailableEvent(this.getEurekaServerConfig()));
                // Set the startup status to true
                this.running = true;
                // Send the Eureka Start event and various other events. We can listen for this time and then do some specific business requirements
                this.publish(new EurekaServerStartedEvent(this.getEurekaServerConfig()));
            } catch (Exception var2) {
                log.error("Could not initialize Eureka servlet context", var2);
            }

        })).start();
    }


    private EurekaServerConfig getEurekaServerConfig(a) {
        return this.eurekaServerConfig;
    }

    private void publish(ApplicationEvent event) {
        this.applicationContext.publishEvent(event);
    }

    public void stop(a) {
        this.running = false;
        this.eurekaServerBootstrap.contextDestroyed(this.servletContext);
    }

    public boolean isRunning(a) {
        return this.running;
    }

    public int getPhase(a) {
        return 0;
    }

    public boolean isAutoStartup(a) {
        return true;
    }

    public void stop(Runnable callback) {
        callback.run();
    }

    public int getOrder(a) {
        return this.order; }}Copy the code

EurekaServerBootstrap

EurekaServerBootstrap’s contextInitialized method

public class EurekaServerBootstrap {
  // Initialize the environment and context of EurekaServer
    public void contextInitialized(ServletContext context) {
        try {
            this.initEurekaEnvironment();
            this.initEurekaServerContext();
            context.setAttribute(EurekaServerContext.class.getName(), this.serverContext);
        } catch (Throwable var3) {
            log.error("Cannot bootstrap eureka server :", var3);
            throw new RuntimeException("Cannot bootstrap eureka server :", var3); }}// Initialize the context of EurekaServer
    protected void initEurekaServerContext(a) throws Exception {
        JsonXStream.getInstance().registerConverter(new V1AwareInstanceInfoConverter(), 10000);
        XmlXStream.getInstance().registerConverter(new V1AwareInstanceInfoConverter(), 10000);
        if (this.isAws(this.applicationInfoManager.getInfo())) {
            this.awsBinder = new AwsBinderDelegate(this.eurekaServerConfig, this.eurekaClientConfig, this.registry, this.applicationInfoManager);
            this.awsBinder.start();
        }
       // Initialize the Eureka Server context
        EurekaServerContextHolder.initialize(this.serverContext);
        log.info("Initialized server context");
       // Copy the registry from the adjacent Eureka node
        int registryCount = this.registry.syncUp();
      // By default, the heartbeat is sent every 30 seconds, 2 beats per minute
      // Change the eureka status to Up 39
      // At the same time, this will start a scheduled task to clean up clients that have no heartbeat for 60 seconds. Automatic logoff
        this.registry.openForTraffic(this.applicationInfoManager, registryCount);
        EurekaMonitors.registerAllStats();
    }

    protected void destroyEurekaServerContext(a) throws Exception {
        EurekaMonitors.shutdown();
        if (this.awsBinder ! =null) {
            this.awsBinder.shutdown();
        }

        if (this.serverContext ! =null) {
            this.serverContext.shutdown(); }}protected void destroyEurekaEnvironment(a) throws Exception {}protected boolean isAws(InstanceInfo selfInstanceInfo) {
        boolean result = Name.Amazon == selfInstanceInfo.getDataCenterInfo().getName();
        log.info("isAws returned " + result);
        returnresult; }}public int syncUp(a) {
        int count = 0;

        for(int i = 0; i < this.serverConfig.getRegistrySyncRetries() && count == 0; ++i) {
            if (i > 0) {
                try {
                    Thread.sleep(this.serverConfig.getRegistrySyncRetryWaitMs());
                } catch (InterruptedException var10) {
                    logger.warn("Interrupted during registry transfer..");
                    break;
                }
            }

            Applications apps = this.eurekaClient.getApplications();
            Iterator var4 = apps.getRegisteredApplications().iterator();

            while(var4.hasNext()) {
                Application app = (Application)var4.next();
                Iterator var6 = app.getInstances().iterator();

                while(var6.hasNext()) {
                    InstanceInfo instance = (InstanceInfo)var6.next();

                    try {
                        if (this.isRegisterable(instance)) {
                            // Register instances of other nodes with this node
                            this.register(instance, instance.getLeaseInfo().getDurationInSecs(), true); ++count; }}catch (Throwable var9) {
                        logger.error("During DS init copy", var9); }}}}return count;
    }



 public void openForTraffic(ApplicationInfoManager applicationInfoManager, int count) {
         // Calculate the maximum number of renewals per minute
        this.expectedNumberOfClientsSendingRenews = count;
        this.updateRenewsPerMinThreshold();
        logger.info("Got {} instances from neighboring DS node", count);
        logger.info("Renew threshold is: {}".this.numberOfRenewsPerMinThreshold);
        this.startupTime = System.currentTimeMillis();
        if (count > 0) {
            this.peerInstancesTransferEmptyOnStartup = false;
        }

        Name selfName = applicationInfoManager.getInfo().getDataCenterInfo().getName();
        boolean isAws = Name.Amazon == selfName;
        if (isAws && this.serverConfig.shouldPrimeAwsReplicaConnections()) {
            logger.info("Priming AWS connections for all replicas..");
            this.primeAwsReplicas(applicationInfoManager);
        }

        logger.info("Changing status to UP");
        // Set the instance status to Up
        applicationInfoManager.setInstanceStatus(InstanceStatus.UP);
        // Start the scheduled task. The scheduled task is executed every 60 seconds by default to clear the instances that are not renewed within 60 seconds
        super.postInit();
    }
Copy the code

From the above EurekaServerAutoConfiguration class, we can see a initialization EurekaServerContext method

    @Bean
    @ConditionalOnMissingBean
    public EurekaServerContext eurekaServerContext(ServerCodecs serverCodecs, PeerAwareInstanceRegistry registry, PeerEurekaNodes peerEurekaNodes) {
        return new DefaultEurekaServerContext(this.eurekaServerConfig, serverCodecs, registry, peerEurekaNodes, this.applicationInfoManager);
    }
Copy the code

DefaultEurekaServerContext this class in the initialize () method is @ PostConstruct this annotation to modify,

This method is executed when the application loads

@PostConstruct public void initialize() { logger.info("Initializing ..." ); / / start a thread, read the other cluster nodes of information, the subsequent copy enclosing peerEurekaNodes. Start (); try { this.registry.init(this.peerEurekaNodes); } catch (Exception var2) { throw new RuntimeException(var2); } logger.info("Initialized"); }Copy the code

Peereurekanodes.start () starts a pool that has only one thread. The first entry updates the information of other nodes in the cluster and then starts a timed thread that updates every 60 seconds. This means that the node configuration can be dynamically changed according to the configuration. (Native Spring Cloud Config support)

public void start(a) {
        this.taskExecutor = Executors.newSingleThreadScheduledExecutor(new ThreadFactory() {
            public Thread newThread(Runnable r) {
                Thread thread = new Thread(r, "Eureka-PeerNodesUpdater");
                thread.setDaemon(true);
                returnthread; }});try {
            // Update the cluster node information for the first time
            this.updatePeerEurekaNodes(this.resolvePeerUrls());
            // Create a thread
            Runnable peersUpdateTask = new Runnable() {
                public void run(a) {
                    try {
                        PeerEurekaNodes.this.updatePeerEurekaNodes(PeerEurekaNodes.this.resolvePeerUrls());
                    } catch (Throwable var2) {
                        PeerEurekaNodes.logger.error("Cannot update the replica Nodes", var2); }}};this.taskExecutor.scheduleWithFixedDelay(peersUpdateTask, (long)this.serverConfig.getPeerEurekaNodesUpdateIntervalMs(), (long)this.serverConfig.getPeerEurekaNodesUpdateIntervalMs(), TimeUnit.MILLISECONDS);
        } catch (Exception var3) {
            throw new IllegalStateException(var3);
        }

        Iterator var4 = this.peerEurekaNodes.iterator();

        while(var4.hasNext()) {
            PeerEurekaNode node = (PeerEurekaNode)var4.next();
            logger.info("Replica node URL: {}", node.getServiceUrl()); }}// Build PeerEurekaNode information based on the URL
protected PeerEurekaNode createPeerEurekaNode(String peerEurekaNodeUrl) {
        HttpReplicationClient replicationClient = JerseyReplicationClient.createReplicationClient(this.serverConfig, this.serverCodecs, peerEurekaNodeUrl);
        String targetHost = hostFromUrl(peerEurekaNodeUrl);
        if (targetHost == null) {
            targetHost = "host";
        }

        return new PeerEurekaNode(this.registry, targetHost, peerEurekaNodeUrl, replicationClient, this.serverConfig);
    }
Copy the code

Eureka Server retention diagram