这是我参与更文挑战的第9天,活动详情查看:更文挑战
一、前言
spring-cloud-starter-eureka-server
项目,就是对 eureka-server
进行了封装的 spring boot
项目。对应的官网:github.com/spring-clou…
可以对照之前 eureka-clien
源码分析:链接
既然都学习了 eureka-client
了,为什么还要学习 spring-cloud-starter-eureka-client
?
- 学习
spring-cloud
如何封装eureka-client
- 学习
spring-cloud
如何启动eureka-client
spring boot
集成 eureka-server
只需要加个注解 @EnableEurekaClient
,代码如下:
@SpringBootApplication
@@EnableEurekaClient
public class EurekaClientApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaClientApplication.class, args);
}
}
小结
问题:
spring-cloud
如何封装eureka-client
?spring-cloud
如何启动eureka-client
?
1. spring-cloud
如何封装 eureka-client
?
启动主要分为两大步:
spring boot
启动后,将内嵌tomcat
容器启动,把自身作为一个web
应用启动- 跟着
@EnableEurekaClient
注解,触发EurekaClientAutoConfiguration
执行:- 从
application.yml
读取配置 - 完成
DiscoveryClient
的初始化和启动 - 触发
register()
服务注册,向eureka-server
注册
- 从
2. spring-cloud
如何启动 eureka-client
?
最容易想到的是,直接拷贝代码。
但拷贝代码,又要减少耦合,保证个模块独立性,那么就需要
spring boot
的特征自动装配。
步骤可分为:
- 从
application.yml
读取配置 - 完成
DiscoveryClient
的初始化和启动
二、直接怼源码
着手点就是
@EnableEurekaClient
。
@EnableEurekaClient
注解:就是在spring boot
将自身的web
容器启动之后,再把eureka-client
启动起来。
从 @EnableEurekaClient
入手,主要看三个类,也可叫为三个步骤:
EurekaClientAutoConfiguration
初始化EurekaAutoServiceRegistration
初始化:对应原来的InstanceInfoReplicator
组件里面的服务注册
在 IDEA
中按 CTRL + 点击
进入 @EnableEurekaServer
,源码如下:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface EnableEurekaClient {
}
EurekaClientAutoConfiguration
初始化
@EnableEurekaClient
会触发 EurekaClientAutoConfiguration
初始化,代码如下:
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties
@ConditionalOnClass(EurekaClientConfig.class)
@ConditionalOnProperty(value = "eureka.client.enabled", matchIfMissing = true)
@ConditionalOnDiscoveryEnabled
@AutoConfigureBefore({ CommonsClientAutoConfiguration.class, ServiceRegistryAutoConfiguration.class })
@AutoConfigureAfter(...)
public class EurekaClientAutoConfiguration {
// 初始化一大堆 eureka client 组件
// 并将这些组件生成对应 bean,加载进 spring 容器中
... ...
}
完成对 DiscoveryClient
的构造和初始化,eureka client
初始化和启动的流程,全部在 DiscoveryClient
中的。
其中 EurekaDiscoveryClient
,spring cloud
对 eureka
原生的 DiscoveryClient
进行了一层封装,同时实现了 eureka
的 DiscoveryClient
接口,依赖了一个原生的 EurekaClient
。
EurekaAutoServiceRegistration
初始化
其源码如下:
public class EurekaAutoServiceRegistration
implements AutoServiceRegistration, SmartLifecycle, Ordered {
// 值得说明的一点:SmartLifecycle ,在 bean 加载初始化完成之后,会调用其 start() 方法。
// 其他参数
... ...
private EurekaServiceRegistry serviceRegistry;
// 通过实现 SmartLifecycle,在 spring 生命周期调用
@Override
public void start() {
... ...
if (!this.running.get() && this.registration.getNonSecurePort() > 0) {
// 服务注册,但里面好像根本没有对应服务注册的逻辑
// 可能因为 eureka-client 代码太烂了,spring-cloud 这帮人也没办法拯救
this.serviceRegistry.register(this.registration);
... ...
}
}
}
// 那么进入 EurekaServiceRegistry.register() 看一眼:
public class EurekaServiceRegistry implements ServiceRegistry<EurekaRegistration> {
... ...
@Override
public void register(EurekaRegistration reg) {
... ...
// ApplicationInfoManager.setInstanceStatus() 调用
// 会通知自己所有的注册的监听器,状态发生改变
// 这个监听器,是 DiscoveryClient.initScheduledTasks() 时候给创建的
reg.getApplicationInfoManager()
.setInstanceStatus(reg.getInstanceConfig().getInitialStatus());
... ...
}
... ...
}
// 最后对照之前 eureka-client 分析,找到:
class InstanceInfoReplicator implements Runnable {
public void start(int initialDelayMs) {
if (this.started.compareAndSet(false, true)) {
this.instanceInfo.setIsDirty();
Future next = this.scheduler
.schedule(this, (long)initialDelayMs, TimeUnit.SECONDS);
this.scheduledPeriodicRef.set(next);
}
}
public boolean onDemandUpdate() {
if (this.rateLimiter.acquire(this.burstSize, (long)this.allowedRatePerMinute)) {
if (!this.scheduler.isShutdown()) {
this.scheduler.submit(new Runnable() {
public void run() {
... ...
// 按理来说,启动线程,不用 run(), 而是 start()
// 这块代码还是比较乱的
InstanceInfoReplicator.this.run();
}
});
return true;
} else {
... ...
}
} else {
... ...
}
}
public void run() {
// 一大堆代码
... ...
this.discoveryClient.register();
... ...
}
}
感觉这块写得还是挺烂的,项目结构不够清晰、调用逻辑混乱、线程调用乱用。