😀 这里写文章的前言: 一个简单的开头,简述这篇文章讨论的问题、目标、人物、背景是什么?并简述你给出的答案。
可以说说你的故事:阻碍、努力、结果成果,意外与转折。
📝 题记 公司的注册中心一直使用的是Eureka,其实在就应该写一篇记录来记录一下或者专门来阅读一下Eureka的源码. SpringCloud全家桶中的组件,如果时间是充足的话,其中的源码阅读下,还是很不错的. 其中是可以学习到很多知识的.
构建项目 : 地址https://github.com/baoyang23/boot-case-self/tree/master/txlcninit , 这里只用看txlcn-server模块的依赖和代码即可,仅仅只有依赖和启动类,并无其他的东西了.
code 项目构建完了,就先启动看下有没有问题,至少保证搭建是ok的. 如果启动没问题的话,那么我们就可以肆无忌惮的去阅读其源码了.
在配置文件中加上打印log的级别和包,这样可以更好的看到源码里面打印的更多的log.
logging: level: com.netflix: DEBUG
初次启动,就可以看下log的输出,定位下是那些类打印的,然后我们可以对那些类进行debug阅读. 这里我们可以看到AbstractInstanceRegistry 这个注册类的输出log
2020-06-27 14:25:55.030 INFO 29855 --- [ main] c.n.e.registry.AbstractInstanceRegistry : Finished initializing remote region registries. All known remote regions: [] 2020-06-27 14:25:55.031 INFO 29855 --- [ main] c.n.eureka.DefaultEurekaServerContext : Initialized 2020-06-27 14:25:55.040 INFO 29855 --- [ main] o.s.b.a.e.web.EndpointLinksResolver : Exposing 2 endpoint(s) beneath base path '/actuator' 2020-06-27 14:25:55.090 INFO 29855 --- [ main] o.s.c.n.e.s.EurekaServiceRegistry : Registering application YANG-EUREKA-SERVER with eureka with status UP
启动
我们在启动类加上 @EnableEurekaServer 这个注解,然后在application.yml中也好配置信息,启动即可. 那么这个注解有这么神奇,就可以直接加载出 Eureka 中的全部内容 ?
@SpringBootApplication @EnableEurekaServer public class TxLcnEurekaServerApplication { public static void main(String[] args) { SpringApplication.run(TxLcnEurekaServerApplication.class,args); } } // 这里看 @Import这个注解,EurekaServerMarkerConfiguration这个类 @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Import(EurekaServerMarkerConfiguration.class) public @interface EnableEurekaServer { } // 这个使用一个配置的注解,然后注入一个marker的bean. 好像也没看到这个bean做了啥事情啊? 咋就能启动Eureka Server端的内容了呢? 真是让人百思不得姐. @Configuration(proxyBeanMethods = false) public class EurekaServerMarkerConfiguration { @Bean public Marker eurekaServerMarkerBean() { return new Marker(); } class Marker { } }
再来看一个东西, spring-cloud-netflix-eureka-server:2.2.1.RELEASE 这个maven的依赖. 2.2.1是引入的版本号.
然后看到 META-INF下spring.factories 这个文件. Spring在启动的时候,会读取这个地方的该文件.所以也就是会读取 org.springframework.cloud.netflix.eureka.server.EurekaServerAutoConfiguration 这个内容. 从名字上看,就是eureka的server端自动配置.
EurekaServerAutoConfiguration
// 先来看下这个类头上的注解是些什么意思. @Configuration(proxyBeanMethods = false) // 这个@Import注解,在出启动的注解中都是有说明使用到的. @Import(EurekaServerInitializerConfiguration.class) //有EurekaServerMarkerConfiguration.Marker.class这个bean的条件,该bean才会继续初始化到Spring容器中. 所以也就是说,如果我们没有加上@EnableEurekaServer注解的,也就不会有Marker这个bean了,所以这里也就不会有了. @ConditionalOnBean(EurekaServerMarkerConfiguration.Marker.class) //二个读取制定前缀的配置文件的类 @EnableConfigurationProperties({ EurekaDashboardProperties.class, InstanceRegistryProperties.class }) // 读取特定文件下的内容 @PropertySource("classpath:/eureka/server.properties") public class EurekaServerAutoConfiguration implements WebMvcConfigurer { } // 然后注入一些bean进来. @Autowired private ApplicationInfoManager applicationInfoManager; /// 注入进来的EurekaServerConfig,可以点进去看到其子类:org.springframework.cloud.netflix.eureka.server.EurekaServerConfigBean,读取eureka.server前缀来头的配置 @Autowired private EurekaServerConfig eurekaServerConfig; //类:org.springframework.cloud.netflix.eureka.EurekaClientConfigBean,这里是读取eureka.client开头的配置信息. @Autowired private EurekaClientConfig eurekaClientConfig; @Autowired private EurekaClient eurekaClient; // 注册一些实例的信息.读取的eureka.instance.registry开头的配置信息. @Autowired private InstanceRegistryProperties instanceRegistryProperties; 然后就是注入一些bean到Spring容器中来. // 注入 PeerAwareInstanceRegistry到Spring容器中. @Bean public PeerAwareInstanceRegistry peerAwareInstanceRegistry( ServerCodecs serverCodecs) { this.eurekaClient.getApplications(); // force initialization // new InstanceRegistry() 方法 return new InstanceRegistry(this.eurekaServerConfig, this.eurekaClientConfig, serverCodecs, this.eurekaClient, this.instanceRegistryProperties.getExpectedNumberOfClientsSendingRenews(), this.instanceRegistryProperties.getDefaultOpenForTrafficCount()); } /** * Register the Jersey filter. * @param eurekaJerseyApp an {@link Application} for the filter to be registered * @return a jersey {@link FilterRegistrationBean} 注入FilterRegistrationBean到Spring容器中. 所有的/eureka/*的请求都要经过这个filter,其他的处理就会走ServletContainer. */ @Bean public FilterRegistrationBean<?> jerseyFilterRegistration( javax.ws.rs.core.Application eurekaJerseyApp) { FilterRegistrationBean<Filter> bean = new FilterRegistrationBean<Filter>(); bean.setFilter(new ServletContainer(eurekaJerseyApp)); bean.setOrder(Ordered.LOWEST_PRECEDENCE); bean.setUrlPatterns( Collections.singletonList(EurekaConstants.DEFAULT_PREFIX + "/*")); return bean; } // 此处省略了详细代码. 添加了 @Path 和 @Provider 二个注解的filter. // 然后添加 /eureka/(font/images/css/js)/*下面的文件信息. 最后注册到Spring容器中. @Bean public javax.ws.rs.core.Application jerseyApplication(Environment environment, ResourceLoader resourceLoader) {....}
org.springframework.cloud.netflix.eureka.server.EurekaServerInitializerConfiguration#start 该方法是如何被调用的? 可以看到这个方法是对 eurekaServerBootstrap进行初始化操作. 并且也有对应的event给发送出去. 在SpringBoot源码中是是可以看到有也有类似的event给publish出去.
@Override public void start () { new Thread (() -> { try { eurekaServerBootstrap.contextInitialized( EurekaServerInitializerConfiguration.this .servletContext); log.info("Started Eureka Server" ); publish(new EurekaRegistryAvailableEvent (getEurekaServerConfig())); EurekaServerInitializerConfiguration.this .running = true ; publish(new EurekaServerStartedEvent (getEurekaServerConfig())); } catch (Exception ex) { log.error("Could not initialize Eureka servlet context" , ex); } }).start(); }
🤗 总结归纳 📎 参考文章
💡 有关文章的问题,欢迎您在底部评论区留言,一起交流~