Eureka Server启动过程

入口:SpringCloud充分利用了SpringBoot的自动装配的特点
  • 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启动过程分析

最新文章

  1. AVA正则表达式4种常用功能
  2. ZeroMQ接口函数之 :zmq_msg_size - 以字节为单位返回消息内容的大小
  3. js获取url方法
  4. spring 管理 jdbc 事务
  5. Spring IoC实现解耦合
  6. tsne降维可视化
  7. MVC 构造新Model实现内容搜索
  8. An AnnotationConfiguration instance is required to use
  9. linux清理僵尸进程
  10. EF Code Frist
  11. Omi教程-组件通讯
  12. Python 项目实践二(生成数据)第一篇
  13. Fiddler安装证书
  14. IOS 命令行工具开发
  15. python进阶(二) 多进程+协程
  16. :命令模式:Command
  17. linux配置sphinx
  18. Rust hello world !
  19. 【八】注入框架RoboGuice使用:(Your First Injected Fragment)
  20. PAT 1024 科学计数法

热门文章

  1. IntelliJ中高效重构的 10 个快捷方式
  2. 数据湖Hudi与对象存储Minio及Hive\Spark\Flink的集成
  3. JSP第二次作业
  4. Nginx01 简介和安装
  5. 制作 2D 素材|基于 AI 5 天创建一个农场游戏,第 4 天
  6. 【已解决】SQL2012启动时报错:cannot find one or more cpmponents
  7. 原生微信小程序封装request
  8. JavaScript 中的 apply、call、bind
  9. ONES 对话 Eolink :数字化企业连接世界的第一接口
  10. JZOJ 4289.Mancity