1. 環境搭建
代碼已經上傳至 https://github.com/masteryourself-tutorial/tutorial-spring,工程是
tutorial-spring-cloud/tutorial-spring-cloud-provider/tutorial-spring-cloud-provider-eureka-5002
2. 源碼解析
詳細的源碼註釋可參考 https://github.com/masteryourself/eureka
2.1 EurekaClientAutoConfiguration 裝配流程
spring-cloud-starter-netflix-eureka-client-xxx.jar
-> spring-cloud-netflix-eureka-client-xxx.jar
-> 定義 spring.factories
文件 -> 向容器中導入 EurekaClientAutoConfiguration
組件,其中配置文件內容如下:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.cloud.netflix.eureka.config.EurekaClientConfigServerAutoConfiguration,\
org.springframework.cloud.netflix.eureka.config.EurekaDiscoveryClientConfigServiceAutoConfiguration,\
org.springframework.cloud.netflix.eureka.EurekaClientAutoConfiguration,\
org.springframework.cloud.netflix.ribbon.eureka.RibbonEurekaAutoConfiguration,\
org.springframework.cloud.netflix.eureka.EurekaDiscoveryClientConfiguration
org.springframework.cloud.bootstrap.BootstrapConfiguration=\
org.springframework.cloud.netflix.eureka.config.EurekaDiscoveryClientConfigServiceBootstrapConfiguration
EurekaClientAutoConfiguration
-> EurekaClientConfigBean
+ DiscoveryClient
(它是一個接口,實際上用的是 CloudEurekaClient
類型)
EurekaClientConfigBean
其實就是配置類,自動關聯 eureka.client
前綴的配置文件
CloudEurekaClient
繼承了 DiscoveryClient
類,注意 DiscoveryClient
類是 netflix 提供的,並不屬於 spring cloud,在構造 CloudEurekaClient
類時,首先會調用父類的構造方法 super(applicationInfoManager, config, args)
,在 DiscoveryClient
的構造函數裏會完成一系列初始化工作
2.2 服務註冊
2.2.1 流程分析
2.2.2 核心源碼剖析
1. com.netflix.discovery.DiscoveryClient#init()
DiscoveryClient(ApplicationInfoManager applicationInfoManager, EurekaClientConfig config, AbstractDiscoveryClientOptionalArgs args,
Provider<BackupRegistry> backupRegistryProvider, EndpointRandomizer endpointRandomizer) {
...
// 獲取註冊信息
// 在 Spring Cloud 中,由 fetch-registry 配置控制 shouldFetchRegistry 屬性,此值默認爲 true,客戶端都爲 true,服務端不需要
if (clientConfig.shouldFetchRegistry() && !fetchRegistry(false)) {
// 獲取失敗,從本地備份恢復
fetchRegistryFromBackup();
}
...
if (clientConfig.shouldRegisterWithEureka() && clientConfig.shouldEnforceRegistrationAtInit()) {
try {
// 向註冊中心註冊服務
if (!register() ) {
throw new IllegalStateException("Registration error at startup. Invalid server response.");
}
} catch (Throwable th) {
logger.error("Registration error at startup: {}", th.getMessage());
throw new IllegalStateException(th);
}
}
// finally, init the schedule tasks (e.g. cluster resolvers, heartbeat, instanceInfo replicator, fetch
// 初始化定時任務,包含 3 個任務
initScheduledTasks();
...
}
2. com.netflix.discovery.DiscoveryClient#fetchRegistry
private boolean fetchRegistry(boolean forceFullRegistryFetch) {
Stopwatch tracer = FETCH_REGISTRY_TIMER.start();
try {
// If the delta is disabled or if it is the first time, get all
// applications
// 第一次的 Applications 是剛剛創建出來的,裏面沒有任何數據
// 後面定時任務觸發的就是增量更新數據
Applications applications = getApplications();
if (clientConfig.shouldDisableDelta()
|| (!Strings.isNullOrEmpty(clientConfig.getRegistryRefreshSingleVipAddress()))
|| forceFullRegistryFetch
|| (applications == null)
|| (applications.getRegisteredApplications().size() == 0)
|| (applications.getVersion() == -1)) //Client application does not have latest library supporting delta
{
...
logger.info("Application version is -1: {}", (applications.getVersion() == -1));
// 全量存儲
getAndStoreFullRegistry();
} else {
// 增量更新數據
getAndUpdateDelta(applications);
}
applications.setAppsHashCode(applications.getReconcileHashCode());
logTotalInstances();
}
...
}
3. com.netflix.discovery.DiscoveryClient#register
boolean register() throws Throwable {
logger.info(PREFIX + "{}: registering service...", appPathIdentifier);
EurekaHttpResponse<Void> httpResponse;
try {
// 向註冊中心註冊
httpResponse = eurekaTransport.registrationClient.register(instanceInfo);
}
...
}
4. com.netflix.discovery.DiscoveryClient#initScheduledTasks
private void initScheduledTasks() {
if (clientConfig.shouldFetchRegistry()) {
// registry cache refresh timer
int registryFetchIntervalSeconds = clientConfig.getRegistryFetchIntervalSeconds();
int expBackOffBound = clientConfig.getCacheRefreshExecutorExponentialBackOffBound();
// 定時刷新註冊表信息
scheduler.schedule(
new TimedSupervisorTask(
"cacheRefresh",
scheduler,
cacheRefreshExecutor,
registryFetchIntervalSeconds,
TimeUnit.SECONDS,
expBackOffBound,
new CacheRefreshThread()
),
registryFetchIntervalSeconds, TimeUnit.SECONDS);
}
if (clientConfig.shouldRegisterWithEureka()) {
int renewalIntervalInSecs = instanceInfo.getLeaseInfo().getRenewalIntervalInSecs();
int expBackOffBound = clientConfig.getHeartbeatExecutorExponentialBackOffBound();
logger.info("Starting heartbeat executor: " + "renew interval is: {}", renewalIntervalInSecs);
// Heartbeat timer
// 定時發送心跳信息
scheduler.schedule(
new TimedSupervisorTask(
"heartbeat",
scheduler,
heartbeatExecutor,
renewalIntervalInSecs,
TimeUnit.SECONDS,
expBackOffBound,
new HeartbeatThread()
),
renewalIntervalInSecs, TimeUnit.SECONDS);
// InstanceInfo replicator
// 創建實例信息複製器線程,定時更新 client 信息給服務端
instanceInfoReplicator = new InstanceInfoReplicator(
this,
instanceInfo,
clientConfig.getInstanceInfoReplicationIntervalSeconds(),
2); // burstSize
...
// 啓動實例信息複製器線程
instanceInfoReplicator.start(clientConfig.getInitialInstanceInfoReplicationIntervalSeconds());
} else {
logger.info("Not registering with Eureka server per configuration");
}
}
2.3 服務下線
2.3.1 流程分析
2.3.2 核心源碼剖析
1. com.netflix.discovery.DiscoveryClient#shutdown
public synchronized void shutdown() {
if (isShutdown.compareAndSet(false, true)) {
logger.info("Shutting down DiscoveryClient ...");
if (statusChangeListener != null && applicationInfoManager != null) {
applicationInfoManager.unregisterStatusChangeListener(statusChangeListener.getId());
}
// 取消定時任務
cancelScheduledTasks();
// If APPINFO was registered
if (applicationInfoManager != null
&& clientConfig.shouldRegisterWithEureka()
&& clientConfig.shouldUnregisterOnShutdown()) {
// 設置狀態爲 down
applicationInfoManager.setInstanceStatus(InstanceStatus.DOWN);
// 服務下線
unregister();
}
if (eurekaTransport != null) {
eurekaTransport.shutdown();
}
heartbeatStalenessMonitor.shutdown();
registryStalenessMonitor.shutdown();
logger.info("Completed shut down of DiscoveryClient");
}
}
2. com.netflix.discovery.DiscoveryClient#cancelScheduledTasks
private void cancelScheduledTasks() {
// 取消實例信息複製器
if (instanceInfoReplicator != null) {
instanceInfoReplicator.stop();
}
// 取消心跳定時任務
if (heartbeatExecutor != null) {
heartbeatExecutor.shutdownNow();
}
// 取消刷新定時任務
if (cacheRefreshExecutor != null) {
cacheRefreshExecutor.shutdownNow();
}
// 取消總的定時任務
if (scheduler != null) {
scheduler.shutdownNow();
}
}