微服务笔记之Euraka(2)
Eureka Server启动过程
- Eureka-server的jar包,发现在MERA-INF下面有配置文件spring.factories
SpringBoot应用启动时会加载EurekaServerAutoConfiguration自动配置
EurekaServerAutoConfiguration类
先看头部信息
@Configuration @Import(EurekaServerInitializerConfiguration.class) //导入初始化类 @ConditionalOnBean(EurekaServerMarkerConfiguration.Marker.class) //加载Bean对象----> Marker @EnableConfigurationProperties({ EurekaDashboardProperties.class, InstanceRegistryProperties.class }) @PropertySource("classpath:/eureka/server.properties") // 配置类和配置信息 public class EurekaServerAutoConfiguration extends WebMvcConfigurerAdapter { //... }
主要分析EurekaServerInitializerConfiguration、Marker、EurekaServerAutoConfiguration这三个类
1、先看Marker这个类,它是什么时候注入的?
其实是由@EnableEurekaServer注解决定的,如下:
@SpringBootApplication @EnableEurekaServer //在我们启动类添加该注解,声明是一个EurekaServer public class EurekaApplication { public static void main(String[] args) { SpringApplication.run(EurekaApplication.class, args); } }
进入EnableEurekaServer注解,会发现他导入了一个EurekaServerMarkerConfiguration类
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Import(EurekaServerMarkerConfiguration.class) // 导入该类,和我们的Marker类很相像 public @interface EnableEurekaServer { }
再进入EurekaServerMarkerConfiguration类,在该类中将Marker注入到容器中
@Configuration public class EurekaServerMarkerConfiguration { @Bean // 此处将Marker注入到容器中 public Marker eurekaServerMarkerBean() { return new Marker(); } class Marker { } }
但是Marker这个类什么都没写,不知道导入它有什么用???
2、再看EurekaServerAutoConfiguration这个类
主要分析以下几个重要的方法
eurekaController这个方法:它注入一个对外的接口(通过这个接口我们可以访问后台界面),这个后台界面我们可以通过配置来修改它是否让它显示,在配置文件中加入eureka.dashboard.enabled = false即可
@Bean @ConditionalOnProperty(prefix = "eureka.dashboard", name = "enabled", matchIfMissing = true) public EurekaController eurekaController() { return new EurekaController(this.applicationInfoManager); }
后台界面就是下图
peerAwareInstanceRegistry这个方法:它是对等节点感知实例注册器,在集群模式下,注册服务使用到的注册器
@Bean public PeerAwareInstanceRegistry peerAwareInstanceRegistry( ServerCodecs serverCodecs) { this.eurekaClient.getApplications(); // force initialization return new InstanceRegistry(this.eurekaServerConfig, this.eurekaClientConfig,serverCodecs, this.eurekaClient, this.instanceRegistryProperties.getExpectedNumberOfClientsSendingRenews(), this.instanceRegistryProperties.getDefaultOpenForTrafficCount()); }
peerEurekaNodes这个方法:它的作用是注入对等的实例节点信息,辅助封装对等节点相关的信息和操作。在EurekaServer集群中,更新集群中对等的节点,因为EurekaServer集群节点可能发生变化,更新节点在PeerEurekaNodes类中的start方法中实现
@Bean @ConditionalOnMissingBean public PeerEurekaNodes peerEurekaNodes(PeerAwareInstanceRegistry registry, ServerCodecs serverCodecs) { return new RefreshablePeerEurekaNodes(registry, this.eurekaServerConfig,this.eurekaClientConfig,serverCodecs, this.applicationInfoManager); }
PeerEurekaNodes类中的start方法
public void start() { this.taskExecutor = Executors.newSingleThreadScheduledExecutor(new ThreadFactory() { public Thread newThread(Runnable r) { Thread thread = new Thread(r, "Eureka-PeerNodesUpdater"); thread.setDaemon(true); return thread; } }); try { this.updatePeerEurekaNodes(this.resolvePeerUrls()); Runnable peersUpdateTask = new Runnable() { public void run() { 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()); } }
那么什么时候执行start方法呢?且往下看
eurekaServerContext这个方法:注入EurekaServer的上下文对象,返回的是一个DefaultEurekaServerContext对象,
@Bean public EurekaServerContext eurekaServerContext(ServerCodecs serverCodecs,PeerAwareInstanceRegistry registry, PeerEurekaNodes peerEurekaNodes) { return new DefaultEurekaServerContext(this.eurekaServerConfig, serverCodecs,registry, peerEurekaNodes, this.applicationInfoManager); }
DefaultEurekaServerContext类中有个Initialize方法,他会在DefaultEurekaServerContext初始化完成后执行,在它里面会执行上述的start方法
@PostConstruct public void initialize() { logger.info("Initializing ..."); this.peerEurekaNodes.start(); try { this.registry.init(this.peerEurekaNodes); } catch (Exception var2) { throw new RuntimeException(var2); } logger.info("Initialized"); }
eurekaServerBootstrap这个方法:注入一个EurekaServerBootstrap对象,后续启动会使用该对象
@Bean public EurekaServerBootstrap eurekaServerBootstrap(PeerAwareInstanceRegistry registry,EurekaServerContext serverContext) { return new EurekaServerBootstrap(this.applicationInfoManager, this.eurekaClientConfig, this.eurekaServerConfig, registry,serverContext); }
jerseyFilterRegistration这个方法:它会注册Jersey过滤器。Jersey它是一个rest框架,帮我们发布resetful服务接口的(类似于springmvc)。EurekaServer端和EurekaClient进行通信,Server端发送的是http服务,该服务就是由Jersey发送。
@Bean public FilterRegistrationBean jerseyFilterRegistration( javax.ws.rs.core.Application eurekaJerseyApp) { FilterRegistrationBean bean = new FilterRegistrationBean(); bean.setFilter(new ServletContainer(eurekaJerseyApp)); bean.setOrder(Ordered.LOWEST_PRECEDENCE); bean.setUrlPatterns(Collections.singletonList(EurekaConstants.DEFAULT_PREFIX + "/*")); return bean; }
jerseyApplication这个方法:它会注入一个Application对象,它的作用就是作为jerseyFilterRegistration方法的参数注册Jersey过滤器
@Bean public javax.ws.rs.core.Application jerseyApplication(Environment environment,ResourceLoader resourceLoader) { //... }
3、再看EurakaServerInitializerConfiguration这个类
它实现了SmartLifecycle这个接口,该接口的父类是Lifecycle(此接口中有一个start方法),实现该接口,可以在Spring容器的Bean创建完成之后做一些事情(如执行start方法)
@Configuration public class EurekaServerInitializerConfiguration implements ServletContextAware, SmartLifecycle, Ordered { //... }
下面我们看一下start方法实现(其中上面我们所讲的EurekaServerBootStrap注册的对象就是在此处使用的)
@Override public void start() { new Thread(new Runnable() { @Override public void run() { try { //TODO: is this class even needed now?
// 初始化EurekaServerContext细节 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) { // Help! log.error("Could not initialize Eureka servlet context", ex); } } }).start();
}
下面看一下contextInitialized这个方法
public void contextInitialized(ServletContext context) { try { // 初始化环境信息 initEurekaEnvironment(); // 初始化context细节 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); } }
看一下initEurekaServerContext这个方法
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(); }
// 此处是给非IOC容器提供获取EurekaServerContext对象的接口 EurekaServerContextHolder.initialize(this.serverContext); log.info("Initialized server context"); // 某一个server实例启动的时候,从集群中其他的server拷贝注册信息过来(同步其他节点信息),每一个server对于其他server来说也是一个客户端
int registryCount = this.registry.syncUp();
// 更改实例状态为UP,对外提供服务
this.registry.openForTraffic(this.applicationInfoManager, registryCount); // 注册统计器 EurekaMonitors.registerAllStats(); }
先看一下syncUp这个方法
public int syncUp() { // Copy entire entry from neighboring DS node int count = 0; // 统计节点个数 // serverConfig.getRegistrySyncRetries() 可能由于远程server没有连接上,做重试操作 for (int i = 0; ((i < serverConfig.getRegistrySyncRetries()) && (count == 0)); i++) { if (i > 0) { try { Thread.sleep(serverConfig.getRegistrySyncRetryWaitMs()); } catch (InterruptedException e) { logger.warn("Interrupted during registry transfer.."); break; } } // 获取其他server的注册表信息 Applications apps = eurekaClient.getApplications(); for (Application app : apps.getRegisteredApplications()) { for (InstanceInfo instance : app.getInstances()) { try { if (isRegisterable(instance)) { // 把从远程获取到的注册表信息注册到自己的注册表中(map) register(instance, instance.getLeaseInfo().getDurationInSecs(), true); count++; } } catch (Throwable t) { logger.error("During DS init copy", t); } } } } return count; }
看一下register方法
回过头来在看一下openForTraffic方法
以上就是EurekaServer启动过程分析