zoukankan      html  css  js  c++  java
  • Spring Cloud Eureka(五):Eureka Server 启动流程分析

    启用EurekaServer

        @SpringBootApplication
    	@EnableEurekaServer
    	public class EurekaApplication {	
    		public static void main(String[] args) {
    			SpringApplication.run(EurekaApplication.class, args);
    		}
    	}
    

    @EnableEurekaServer 源码:

        @Target(ElementType.TYPE)
    	@Retention(RetentionPolicy.RUNTIME)
    	@Documented
    	@Import(EurekaServerMarkerConfiguration.class)
    	public @interface EnableEurekaServer {
    	
    	}
    

    将配置类 EurekaServerMarkerConfiguration 加入spring 容器。

    EurekaServerMarkerConfiguration 的源码

    /**
     *
     * 激活 EurekaServerAutoConfiguration 的开关
     */
    @Configuration
    public class EurekaServerMarkerConfiguration {
    
    	@Bean
    	public Marker eurekaServerMarkerBean() {
    		return new Marker();
    	}
    
    	class Marker {
    	}
    }
    

    我们看到只是实例化了一个空类,没有任何实现,从注释中可以看到 EurekaServerMarkerConfiguration 是一个激活 EurekaServerAutoConfiguration 的开关。我们在项目源码的 META-INF/spring.factories中看到

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

    真正的配置信息在 EurekaServerAutoConfiguration 中,我们看到 @ConditionalOnBean(EurekaServerMarkerConfiguration.Marker.class) 只有存在 Marker 实例的时候,才会继续加载配置项,这也就要求必须有 @EnableEurekaServer 注解,才能正常的启动。

    @Configuration
    @Import(EurekaServerInitializerConfiguration.class)
    @ConditionalOnBean(EurekaServerMarkerConfiguration.Marker.class)
    @EnableConfigurationProperties({ EurekaDashboardProperties.class,
    		InstanceRegistryProperties.class })
    @PropertySource("classpath:/eureka/server.properties")
    public class EurekaServerAutoConfiguration extends WebMvcConfigurerAdapter {
    
    }
    

    简单的梳理一下 @EnableEurekaServer 的流程为:

    import
    扫描到
    条件判断
    加载具体配置信息
    EnableEurekaServer
    EurekaServerMarkerConfiguration
    EurekaServerAutoConfiguration
    ConditionalOnBean
    EurekaServerAutoConfiguration

    EurekaServerAutoConfiguration 源码

    在源码中我们可以看到一个很重要的注解 @Import(EurekaServerInitializerConfiguration.class) ,该注解是真正的的启动了服务,下面我们就看一下它都有哪些操作

    @Configuration
    @Import(EurekaServerInitializerConfiguration.class)
    @ConditionalOnBean(EurekaServerMarkerConfiguration.Marker.class)
    @EnableConfigurationProperties({ EurekaDashboardProperties.class,
    		InstanceRegistryProperties.class })
    @PropertySource("classpath:/eureka/server.properties")
    public class EurekaServerAutoConfiguration extends WebMvcConfigurerAdapter {
    
    }
    

    1、@Configuration 表明这是一个配置类
    2、@Import(EurekaServerInitializerConfiguration.class) 导入启动EurekaServer的bean
    3、@ConditionalOnBean(EurekaServerMarkerConfiguration.Marker.class) 这个是表示只有在spring容器
    里面含有Market这个实例的时候,才会加载当前这个Bean(EurekaServerAutoConfiguration ),这个就是控制
    是否开启EurekaServer的关键,

    4、@EnableConfigurationProperties({ EurekaDashboardProperties.class, InstanceRegistryProperties.class })
    加载配置
    5、@PropertySource(“classpath:/eureka/server.properties”) 加载配置文件。

    EurekaServerInitializerConfiguration 实现了SmartLifecycle.start方法,在spring 初始化的时候,会被调用

    @Configuration
    @CommonsLog
    public class EurekaServerInitializerConfiguration
          implements ServletContextAware, SmartLifecycle, Ordered {
     
       @Autowired
       private EurekaServerConfig eurekaServerConfig;
     
       private ServletContext servletContext;
     
       @Autowired
       private ApplicationContext applicationContext;
     
       @Autowired
       private EurekaServerBootstrap eurekaServerBootstrap;
     
       private boolean running;
     
       private int order = 1;
     
       @Override
       public void setServletContext(ServletContext servletContext) {
          this.servletContext = servletContext;
       }
     
       @Override
       public void start() {
          // 启动一个线程
          new Thread(new Runnable() {
             @Override
             public void run() {
                try {
                   //初始化EurekaServer,同时启动Eureka Server
                   eurekaServerBootstrap.contextInitialized(EurekaServerInitializerConfiguration.this.servletContext);
                   log.info("Started Eureka Server");
                    // 发布EurekaServer的注册事件
                   publish(new EurekaRegistryAvailableEvent(getEurekaServerConfig()));
                    // 设置启动的状态为true
                   EurekaServerInitializerConfiguration.this.running = true;
                    // 发布Eureka Start 事件,另外还有其他事件,我们可以根据需要监听这些事件,然后做一些特定的业务需求
                   publish(new EurekaServerStartedEvent(getEurekaServerConfig()));
                }
                catch (Exception ex) {
                  
                   log.error("Could not initialize Eureka servlet context", ex);
                }
             }
          }).start();
       }
     
       private EurekaServerConfig getEurekaServerConfig() {
          return this.eurekaServerConfig;
       }
     
       private void publish(ApplicationEvent event) {
          this.applicationContext.publishEvent(event);
       }
     
       @Override
       public void stop() {
          this.running = false;
          eurekaServerBootstrap.contextDestroyed(this.servletContext);
       }
     
      .................省略...............
    }
    

    eurekaServerBootstrap是spring cloud定义的类,其代码完全拷贝了Eureka启动类的实现。 这里看到在Eureka启动后,会向外发送EurekaRegistryAvailableEvent 和 EurekaServerStartedEvent 事件,我们可以根据需要订阅这两个事件,做具体的处理操作。

    接着我们一下 EurekaServerBootstrap 做了哪些具体的操作,从源码中可以看到方法 contextInitialized(ServletContext context) 进行了环境和上下文的初始化工作,下面我们分别看一下:

    EurekaServerBootstrap > contextInitialized

    public void contextInitialized(ServletContext context) {
    		try {
                //初始化执行环境
    			initEurekaEnvironment();
                //初始化上下文
    			initEurekaServerContext();
    
    			context.setAttribute(EurekaServerContext.class.getName(), this.serverContext);
    		}
    		catch (Throwable e) {
    			log.error("Cannot bootstrap eureka server :", e);
    			throw new RuntimeException("Cannot bootstrap eureka server :", e);
    		}
    	}
    

    EurekaServerBootstrap > contextInitialized > initEurekaEnvironment

    protected void initEurekaEnvironment() throws Exception {
    		log.info("Setting the eureka configuration..");
    
            //设置数据中心
    		String dataCenter = ConfigurationManager.getConfigInstance()
    				.getString(EUREKA_DATACENTER);
    		if (dataCenter == null) {
    			log.info(
    					"Eureka data center value eureka.datacenter is not set, defaulting to default");
    			ConfigurationManager.getConfigInstance()
    					.setProperty(ARCHAIUS_DEPLOYMENT_DATACENTER, DEFAULT);
    		}
    		else {
    			ConfigurationManager.getConfigInstance()
    					.setProperty(ARCHAIUS_DEPLOYMENT_DATACENTER, dataCenter);
    		}
            //设置 Eureka 环境
    		String environment = ConfigurationManager.getConfigInstance()
    				.getString(EUREKA_ENVIRONMENT);
    		if (environment == null) {
    			ConfigurationManager.getConfigInstance()
    					.setProperty(ARCHAIUS_DEPLOYMENT_ENVIRONMENT, TEST);
    			log.info(
    					"Eureka environment value eureka.environment is not set, defaulting to test");
    		}
    		else {
    			ConfigurationManager.getConfigInstance()
    					.setProperty(ARCHAIUS_DEPLOYMENT_ENVIRONMENT, environment);
    		}
    	}
    

    EurekaServerBootstrap > contextInitialized > initEurekaEnvironment

    protected void initEurekaServerContext() throws Exception {
    		// For backward compatibility
    		JsonXStream.getInstance().registerConverter(new V1AwareInstanceInfoConverter(),
    				XStream.PRIORITY_VERY_HIGH);
    		XmlXStream.getInstance().registerConverter(new V1AwareInstanceInfoConverter(),
    				XStream.PRIORITY_VERY_HIGH);
    
    		if (isAws(this.applicationInfoManager.getInfo())) {
    			this.awsBinder = new AwsBinderDelegate(this.eurekaServerConfig,
    					this.eurekaClientConfig, this.registry, this.applicationInfoManager);
    			this.awsBinder.start();
    		}
    
    		EurekaServerContextHolder.initialize(this.serverContext);
    
    		log.info("Initialized server context");
    
    		// 从邻近的eureka节点复制注册表
    		int registryCount = this.registry.syncUp();
    		this.registry.openForTraffic(this.applicationInfoManager, registryCount);
    
    		// 注册所有监控统计信息
    		EurekaMonitors.registerAllStats();
    	}
    
    

    至此,EurekaServer启动完毕, EurekaServerBootstrap是完全复制了原生EurekaBootstrap的代码, 因为原生的Eureka
    是在servlet应用,但是spring cloud的应用是运行在内嵌的Tomcat等WEB服务器里面的,所以就使用EurekaServerBootstrap
    来做替换,最终使Eureka能够在spring boot中使用。

  • 相关阅读:
    自定义Visual Studio调试器中的对象显示方式
    Visual Studio中的主题定制变得更加容易
    宣布Visual Studio Code Installer for Java
    在Visual Studio 2019中开启预览功能
    .NET Core 3.0预览版7中的ASP.NET Core和Blazor更新
    C#连接SQL Anywhere 12 数据库
    数据库导出脚本
    python http请求及多线程应用
    记录 Ext 日历月份选择控件bug解决过程结果
    js浮点数运算封装, 起因财务部分精确计算
  • 原文地址:https://www.cnblogs.com/liukaifeng/p/10052590.html
Copyright © 2011-2022 走看看