SpringCloud輪詢拉取注冊(cè)表與服務(wù)發(fā)現(xiàn)流程詳解
一、前言
上一篇我們討論了關(guān)于周期性任務(wù)的一些應(yīng)用等,本篇文章我們來(lái)探究一下這些內(nèi)容:周期性刷新注冊(cè)表?全量拉取注冊(cè)表還是增量拉取注冊(cè)表、更新本地緩存?服務(wù)發(fā)現(xiàn)的入口、獲取本地服務(wù)列表?
二、輪詢拉取注冊(cè)表
1、構(gòu)造初始化
同樣是在Spring容器初始化的過(guò)程中初始化的,基于SpringBoot自動(dòng)裝配集成。上一節(jié)也講了一部分,這里補(bǔ)充:
@Singleton
public class DiscoveryClient implements EurekaClient {
....省略n行代碼......
private final ScheduledExecutorService scheduler;
// additional executors for supervised subtasks監(jiān)督子任務(wù)的附加執(zhí)行器
private final ThreadPoolExecutor heartbeatExecutor;
private final ThreadPoolExecutor cacheRefreshExecutor;
private TimedSupervisorTask cacheRefreshTask;
private TimedSupervisorTask heartbeatTask;
....省略n行代碼......
// Spring容器初始化時(shí)候調(diào)用
public DiscoveryClient(ApplicationInfoManager applicationInfoManager,
final EurekaClientConfig config, AbstractDiscoveryClientOptionalArgs args) {
// 調(diào)用下面重載方法
this(applicationInfoManager, config, args, ResolverUtils::randomize);
}
public DiscoveryClient(ApplicationInfoManager applicationInfoManager,
final EurekaClientConfig config,
AbstractDiscoveryClientOptionalArgs args, EndpointRandomizer randomizer) {
this(applicationInfoManager, config, args, new Provider<BackupRegistry>() {
....省略n行代碼......
}
@Inject
DiscoveryClient(ApplicationInfoManager applicationInfoManager, EurekaClientConfig config,
AbstractDiscoveryClientOptionalArgs args,
Provider<BackupRegistry> backupRegistryProvider, EndpointRandomizer endpointRandomizer) {
try {
// default size of 2 - 1 each for heartbeat and cacheRefresh心跳和緩存刷新的默認(rèn)大小分別為2-1
scheduler = Executors.newScheduledThreadPool(2,
new ThreadFactoryBuilder()
.setNameFormat("DiscoveryClient-%d")
.setDaemon(true)
.build());
// 心跳執(zhí)行者
heartbeatExecutor = new ThreadPoolExecutor(
1, clientConfig.getHeartbeatExecutorThreadPoolSize(), 0, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>(),
new ThreadFactoryBuilder()
.setNameFormat("DiscoveryClient-HeartbeatExecutor-%d")
.setDaemon(true)
.build()
); // use direct handoff
// 緩存刷新執(zhí)行者
cacheRefreshExecutor = new ThreadPoolExecutor(
1, clientConfig.getCacheRefreshExecutorThreadPoolSize(), 0, TimeUnit.SECONDS,
new SynchronousQueue<Runnable>(),
new ThreadFactoryBuilder()
.setNameFormat("DiscoveryClient-CacheRefreshExecutor-%d")
.setDaemon(true)
.build()
); // use direct handoff
// 初始化通信封裝類
eurekaTransport = new EurekaTransport();
....省略n行代碼......
} catch (Throwable e) {
throw new RuntimeException("Failed to initialize DiscoveryClient!", e);
}
// 默認(rèn)true,可更改配置不建議
if (clientConfig.shouldFetchRegistry()) {
try {// 初始化注冊(cè)表
boolean primaryFetchRegistryResult = fetchRegistry(false);
// 下面主要打印失敗日志,初始化時(shí)控制臺(tái)可見是處理成功的
if (!primaryFetchRegistryResult) {
// 從主服務(wù)器初始注冊(cè)表提取失敗
logger.info("Initial registry fetch from primary servers failed");
}
boolean backupFetchRegistryResult = true;
if (!primaryFetchRegistryResult && !fetchRegistryFromBackup()) {
// 如果所有的eureka服務(wù)器網(wǎng)址都無(wú)法訪問,從備份注冊(cè)表中獲取注冊(cè)表信息也失敗。
backupFetchRegistryResult = false;
// 從備份服務(wù)器初始注冊(cè)表提取失敗
logger.info("Initial registry fetch from backup servers failed");
}
if (!primaryFetchRegistryResult && !backupFetchRegistryResult && clientConfig.shouldEnforceFetchRegistryAtInit()) {
// 在啟動(dòng)時(shí)獲取注冊(cè)表錯(cuò)誤。初始獲取失敗。
throw new IllegalStateException("Fetch registry error at startup. Initial fetch failed.");
}
} catch (Throwable th) {
logger.error("Fetch registry error at startup: {}", th.getMessage());
throw new IllegalStateException(th);
}
}
....省略n行代碼......
// 最后,初始化調(diào)度任務(wù)(例如,集群解析器、 heartbeat、 instanceInfo replicator、 fetch
initScheduledTasks();
....省略n行代碼......
}主要邏輯:
- 初始化緩存刷新執(zhí)行器,用于周期性執(zhí)行任務(wù),下面繼續(xù)分析
- 默認(rèn)需要刷新注冊(cè)表,默認(rèn)不使用全量拉取,但是初始化時(shí)使用下面2分析:會(huì)調(diào)用注冊(cè)中心完成注冊(cè)表初始化,返回是否刷新成功;如果從主服務(wù)器初始注冊(cè)表提取失敗打印日志;如果所有的eureka服務(wù)器網(wǎng)址都無(wú)法訪問,從備份注冊(cè)表中獲取注冊(cè)表信息也失敗打印日志;在啟動(dòng)時(shí)獲取注冊(cè)表錯(cuò)誤,拋異常。
- 可見在2中沒有特殊原因的話,一般是使用全量拉取注冊(cè)表初始化成功了,否則的話拋異常
緩存刷新
大部分邏輯在前面的章節(jié)已經(jīng)分析,TimedSupervisorTask跟發(fā)送心跳服務(wù)續(xù)約邏輯是一樣的,這里補(bǔ)充刷新本地服務(wù)列表任務(wù)。
private void initScheduledTasks() {
// 默認(rèn)true,可更改配置不建議
if (clientConfig.shouldFetchRegistry()) {
// registry cache refresh timer注冊(cè)表緩存刷新計(jì)時(shí)器
// 默認(rèn)30
int registryFetchIntervalSeconds = clientConfig.getRegistryFetchIntervalSeconds();
int expBackOffBound = clientConfig.getCacheRefreshExecutorExponentialBackOffBound();
cacheRefreshTask = new TimedSupervisorTask(
"cacheRefresh",
scheduler,
cacheRefreshExecutor,
registryFetchIntervalSeconds,
TimeUnit.SECONDS,
expBackOffBound,
new CacheRefreshThread()
);
scheduler.schedule(
cacheRefreshTask,
registryFetchIntervalSeconds, TimeUnit.SECONDS);
}
if (clientConfig.shouldRegisterWithEureka()) {
/* LeaseInfo:
public static final int DEFAULT_LEASE_RENEWAL_INTERVAL = 30;
// Client settings
private int renewalIntervalInSecs = DEFAULT_LEASE_RENEWAL_INTERVAL;
*/
// 默認(rèn)30
int renewalIntervalInSecs = instanceInfo.getLeaseInfo().getRenewalIntervalInSecs();
int expBackOffBound = clientConfig.getHeartbeatExecutorExponentialBackOffBound();
logger.info("Starting heartbeat executor: " + "renew interval is: {}", renewalIntervalInSecs);
// Heartbeat timer心跳任務(wù)
heartbeatTask = new TimedSupervisorTask(
"heartbeat",
scheduler,
heartbeatExecutor,
renewalIntervalInSecs,
TimeUnit.SECONDS,
expBackOffBound,
new HeartbeatThread()
);
// 默認(rèn)的情況下會(huì)每隔30秒向注冊(cè)中心 (eureka.instance.lease-renewal-interval-in-seconds)發(fā)送一次心跳來(lái)進(jìn)行服務(wù)續(xù)約
scheduler.schedule(
heartbeatTask,
renewalIntervalInSecs, TimeUnit.SECONDS);
// InstanceInfo replicator實(shí)例信息復(fù)制任務(wù)
instanceInfoReplicator = new InstanceInfoReplicator(
this,
instanceInfo,
clientConfig.getInstanceInfoReplicationIntervalSeconds(),
2); // burstSize
// 狀態(tài)變更監(jiān)聽者
statusChangeListener = new ApplicationInfoManager.StatusChangeListener() {
@Override
public String getId() {
return "statusChangeListener";
}
@Override
public void notify(StatusChangeEvent statusChangeEvent) {
// Saw local status change event StatusChangeEvent [timestamp=1668595102513, current=UP, previous=STARTING]
logger.info("Saw local status change event {}", statusChangeEvent);
instanceInfoReplicator.onDemandUpdate();
}
};
// 初始化狀態(tài)變更監(jiān)聽者
if (clientConfig.shouldOnDemandUpdateStatusChange()) {
applicationInfoManager.registerStatusChangeListener(statusChangeListener);
}
// 3.2 定時(shí)刷新服務(wù)實(shí)例信息和檢查應(yīng)用狀態(tài)的變化,在服務(wù)實(shí)例信息發(fā)生改變的情況下向server重新發(fā)起注冊(cè)
instanceInfoReplicator.start(clientConfig.getInitialInstanceInfoReplicationIntervalSeconds());
} else {
logger.info("Not registering with Eureka server per configuration");
}
}主要邏輯:
默認(rèn)需要刷新注冊(cè)表,要達(dá)到服務(wù)高可用。
1)clientConfig.getRegistryFetchIntervalSeconds()獲取注冊(cè)表刷新時(shí)間,默認(rèn)30秒,可在配置文件更改;
2)初始化cacheRefreshTask為TimedSupervisorTask類型,跟心跳任務(wù)一樣處理邏輯,我們這節(jié)就只分析CacheRefreshThread刷新緩存邏輯,見3
2、刷新注冊(cè)表
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如果 delta 被禁用或者是第一次,那么獲取所有的應(yīng)用程序
Applications applications = getApplications();
// shouldDisableDelta默認(rèn)false
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("Disable delta property false: {}", clientConfig.shouldDisableDelta());
logger.info("Single vip registry refresh property null: {}", clientConfig.getRegistryRefreshSingleVipAddress());
logger.info("Force full registry fetch false: {}", forceFullRegistryFetch);
logger.info("Application is null false: {}", (applications == null));
logger.info("Registered Applications size is zero true: {}",
(applications.getRegisteredApplications().size() == 0));
logger.info("Application version is -1true: {}", (applications.getVersion() == -1));
// 全量拉取
getAndStoreFullRegistry();
} else {
// 增量拉取
getAndUpdateDelta(applications);
}
applications.setAppsHashCode(applications.getReconcileHashCode());
logTotalInstances();
} catch (Throwable e) {
logger.info(PREFIX + "{} - was unable to refresh its cache! This periodic background refresh will be retried in {} seconds. status = {} stacktrace = {}",
appPathIdentifier, clientConfig.getRegistryFetchIntervalSeconds(), e.getMessage(), ExceptionUtils.getStackTrace(e));
return false;
} finally {
if (tracer != null) {
tracer.stop();
}
}
// Notify about cache refresh before updating the instance remote status
// 在更新實(shí)例遠(yuǎn)程狀態(tài)之前通知緩存刷新
onCacheRefreshed();
// Update remote status based on refreshed data held in the cache
// 根據(jù)緩存中保存的刷新數(shù)據(jù)更新遠(yuǎn)程狀態(tài)
updateInstanceRemoteStatus();
// registry was fetched successfully, so return true
return true;
}主要邏輯:
- 由上面的1.1可見狀態(tài)變更監(jiān)聽者還沒有初始化,從前面的文章也知道它的作用完成服務(wù)注冊(cè),故這里從本地獲取應(yīng)用就為空。所以先打印下日志,調(diào)用全量拉取注冊(cè)表方法,下面分析。軌跡跟蹤對(duì)象非空,關(guān)閉。
- 在更新實(shí)例遠(yuǎn)程狀態(tài)之前通知緩存刷新
- 根據(jù)緩存中保存的刷新數(shù)據(jù)更新遠(yuǎn)程狀態(tài)
全量拉取注冊(cè)表
private void getAndStoreFullRegistry() throws Throwable {
long currentUpdateGeneration = fetchRegistryGeneration.get();
// 從 eureka 服務(wù)器上獲取所有實(shí)例注冊(cè)信息
logger.info("Getting all instance registry info from the eureka server");
Applications apps = null;
// RegistryRefreshSingleVipAddress默認(rèn)空
EurekaHttpResponse<Applications> httpResponse = clientConfig.getRegistryRefreshSingleVipAddress() == null
? eurekaTransport.queryClient.getApplications(remoteRegionsRef.get())
: eurekaTransport.queryClient.getVip(clientConfig.getRegistryRefreshSingleVipAddress(), remoteRegionsRef.get());
// 響應(yīng)成功獲取應(yīng)用
if (httpResponse.getStatusCode() == Status.OK.getStatusCode()) {
apps = httpResponse.getEntity();
}
// 200
logger.info("The response status is {}", httpResponse.getStatusCode());
if (apps == null) {
logger.error("The application is null for some reason. Not storing this information");
} else if (fetchRegistryGeneration.compareAndSet(currentUpdateGeneration, currentUpdateGeneration + 1)) {
// 緩存到本地
localRegionApps.set(this.filterAndShuffle(apps));
// 如:UP_1_
logger.debug("Got full registry with apps hashcode {}", apps.getAppsHashCode());
} else {
logger.warn("Not updating applications as another thread is updating it already");
}
}主要邏輯:
- RegistryRefreshSingleVipAddress默認(rèn)空,故調(diào)用eurekaTransport.queryClient.getApplications(remoteRegionsRef.get())請(qǐng)求注冊(cè)中心
- 響應(yīng)成功獲取應(yīng)用
- 如果應(yīng)用apps空則打印下日志;一般CAS成功,在篩選僅具有 UP 狀態(tài)的實(shí)例的應(yīng)用程序并對(duì)它們進(jìn)行洗牌之后獲取應(yīng)用程序,緩存到本地localRegionApps(AtomicReference<Applications>類型);否則打印下日志
3、緩存刷新任務(wù)
class CacheRefreshThread implements Runnable {
@Override
public void run() {
refreshRegistry();
}
}
@VisibleForTesting
void refreshRegistry() {
try {
boolean isFetchingRemoteRegionRegistries = isFetchingRemoteRegionRegistries();
boolean remoteRegionsModified = false;
// This makes sure that a dynamic change to remote regions to fetch is honored.
// 這可以確保對(duì)要獲取的遠(yuǎn)程區(qū)域的動(dòng)態(tài)更改得到遵守。
// 默認(rèn)null
String latestRemoteRegions = clientConfig.fetchRegistryForRemoteRegions();
if (null != latestRemoteRegions) {
String currentRemoteRegions = remoteRegionsToFetch.get();
if (!latestRemoteRegions.equals(currentRemoteRegions)) {
// Both remoteRegionsToFetch and AzToRegionMapper.regionsToFetch need to be in sync
// RemoteRegionsToFetch 和 AzToRegionMapper.regionsToFetch 都需要同步
synchronized (instanceRegionChecker.getAzToRegionMapper()) {
// CAS
if (remoteRegionsToFetch.compareAndSet(currentRemoteRegions, latestRemoteRegions)) {
String[] remoteRegions = latestRemoteRegions.split(",");
remoteRegionsRef.set(remoteRegions);
instanceRegionChecker.getAzToRegionMapper().setRegionsToFetch(remoteRegions);
remoteRegionsModified = true;
} else {
// 并發(fā)獲取修改的遠(yuǎn)程區(qū)域,忽略從{}到{}的更改
logger.info("Remote regions to fetch modified concurrently," +
" ignoring change from {} to {}", currentRemoteRegions, latestRemoteRegions);
}
}
} else {
// Just refresh mapping to reflect any DNS/Property change
// 只需刷新映射以反映任何 DNS/屬性更改
instanceRegionChecker.getAzToRegionMapper().refreshMapping();
}
}
// 刷新注冊(cè)表
boolean success = fetchRegistry(remoteRegionsModified);
if (success) {
registrySize = localRegionApps.get().size();
lastSuccessfulRegistryFetchTimestamp = System.currentTimeMillis();
}
if (logger.isDebugEnabled()) {
StringBuilder allAppsHashCodes = new StringBuilder();
allAppsHashCodes.append("Local region apps hashcode: ");
allAppsHashCodes.append(localRegionApps.get().getAppsHashCode());
allAppsHashCodes.append(", is fetching remote regions? ");
allAppsHashCodes.append(isFetchingRemoteRegionRegistries);
for (Map.Entry<String, Applications> entry : remoteRegionVsApps.entrySet()) {
allAppsHashCodes.append(", Remote region: ");
allAppsHashCodes.append(entry.getKey());
allAppsHashCodes.append(" , apps hashcode: ");
allAppsHashCodes.append(entry.getValue().getAppsHashCode());
}
logger.debug("Completed cache refresh task for discovery. All Apps hash code is {} ",
allAppsHashCodes);
}
} catch (Throwable e) {
logger.error("Cannot fetch registry from server", e);
}
}CacheRefreshThread 實(shí)現(xiàn)了Runnable接口,但是run()中任務(wù)邏輯封裝了出去,在refreshRegistry()中處理。在2中分析了初始化時(shí)已經(jīng)使用全量拉取注冊(cè)表并緩存應(yīng)用到本地localRegionApps,那么這里使用延遲任務(wù)處理的話就會(huì)執(zhí)行增量拉取邏輯了,在下面4分析
4、增量拉取注冊(cè)表
private void getAndUpdateDelta(Applications applications) throws Throwable {
long currentUpdateGeneration = fetchRegistryGeneration.get();
Applications delta = null;
// 增量查詢獲取
EurekaHttpResponse<Applications> httpResponse = eurekaTransport.queryClient.getDelta(remoteRegionsRef.get());
// 響應(yīng)成功
if (httpResponse.getStatusCode() == Status.OK.getStatusCode()) {
delta = httpResponse.getEntity();
}
if (delta == null) {
// 服務(wù)器不允許應(yīng)用delta修訂,因?yàn)樗话踩?。因此得到了完整的登記表,即轉(zhuǎn)換為全量拉取
logger.warn("The server does not allow the delta revision to be applied because it is not safe. "
+ "Hence got the full registry.");
getAndStoreFullRegistry();
} else if (fetchRegistryGeneration.compareAndSet(currentUpdateGeneration, currentUpdateGeneration + 1)) {
// CAS成功
logger.debug("Got delta update with apps hashcode {}", delta.getAppsHashCode());
String reconcileHashCode = "";
if (fetchRegistryUpdateLock.tryLock()) {
// 加鎖成功
try {
updateDelta(delta);
reconcileHashCode = getReconcileHashCode(applications);
} finally {
fetchRegistryUpdateLock.unlock();
}
} else {
logger.warn("Cannot acquire update lock, aborting getAndUpdateDelta");
}
// There is a diff in number of instances for some reason出于某種原因,數(shù)量有所不同
if (!reconcileHashCode.equals(delta.getAppsHashCode()) || clientConfig.shouldLogDeltaDiff()) {
reconcileAndLogDifference(delta, reconcileHashCode); // this makes a remoteCall這個(gè)可以遠(yuǎn)程呼叫
}
} else {
logger.warn("Not updating application delta as another thread is updating it already");
logger.debug("Ignoring delta update with apps hashcode {}, as another thread is updating it already", delta.getAppsHashCode());
}
}主要邏輯:
- 增量查詢獲取,響應(yīng)成功獲取數(shù)據(jù)
- CAS成功并且加鎖成功,將響應(yīng)結(jié)果更新到本地,然后釋放鎖
增量更新到本地緩存
private void updateDelta(Applications delta) {
int deltaCount = 0;
for (Application app : delta.getRegisteredApplications()) {
for (InstanceInfo instance : app.getInstances()) {
// 從本地獲取,以便更新
Applications applications = getApplications();
String instanceRegion = instanceRegionChecker.getInstanceRegion(instance);
if (!instanceRegionChecker.isLocalRegion(instanceRegion)) {
Applications remoteApps = remoteRegionVsApps.get(instanceRegion);
if (null == remoteApps) {
remoteApps = new Applications();
remoteRegionVsApps.put(instanceRegion, remoteApps);
}
applications = remoteApps;
}
++deltaCount;
if (ActionType.ADDED.equals(instance.getActionType())) {
Application existingApp = applications.getRegisteredApplications(instance.getAppName());
if (existingApp == null) {
applications.addApplication(app);
}
// 將實(shí)例{}添加到區(qū)域{}中的現(xiàn)有應(yīng)用程序
// ceam-config:8888,ceam-auth:8005,region null
logger.debug("Added instance {} to the existing apps in region {}", instance.getId(), instanceRegion);
// 即添加到Application的instancesMap
applications.getRegisteredApplications(instance.getAppName()).addInstance(instance);
} else if (ActionType.MODIFIED.equals(instance.getActionType())) {
Application existingApp = applications.getRegisteredApplications(instance.getAppName());
if (existingApp == null) {
applications.addApplication(app);
}
// 修改現(xiàn)有應(yīng)用程序的實(shí)例{}
logger.debug("Modified instance {} to the existing apps ", instance.getId());
applications.getRegisteredApplications(instance.getAppName()).addInstance(instance);
} else if (ActionType.DELETED.equals(instance.getActionType())) {
Application existingApp = applications.getRegisteredApplications(instance.getAppName());
if (existingApp != null) {
// 刪除現(xiàn)有應(yīng)用程序的實(shí)例{}
logger.debug("Deleted instance {} to the existing apps ", instance.getId());
existingApp.removeInstance(instance);
/*
* We find all instance list from application(The status of instance status is not only the status is UP but also other status)
* if instance list is empty, we remove the application.
* 我們從應(yīng)用程序中找到所有的實(shí)例列表(實(shí)例狀態(tài)的狀態(tài)不僅是狀態(tài)是 UP,
* 還有其他狀態(tài))如果實(shí)例列表為空,我們刪除應(yīng)用程序。
*/
if (existingApp.getInstancesAsIsFromEureka().isEmpty()) {
applications.removeApplication(existingApp);
}
}
}
}
}
logger.debug("The total number of instances fetched by the delta processor : {}", deltaCount);
getApplications().setVersion(delta.getVersion());
// 對(duì)提供的實(shí)例進(jìn)行洗牌,以便它們不總是以相同的順序返回。
getApplications().shuffleInstances(clientConfig.shouldFilterOnlyUpInstances());
for (Applications applications : remoteRegionVsApps.values()) {
applications.setVersion(delta.getVersion());
// 對(duì)提供的實(shí)例進(jìn)行洗牌,以便它們不總是以相同的順序返回。
applications.shuffleInstances(clientConfig.shouldFilterOnlyUpInstances());
}
}主要邏輯:
- 遍歷增量的應(yīng)用數(shù)據(jù),遍歷應(yīng)用中的實(shí)例
- 從本地獲取應(yīng)用數(shù)據(jù),以便更新處理
- 如果是ADDED,則將實(shí)例添加到區(qū)域中的現(xiàn)有應(yīng)用程序;如果是MODIFIED,則修改現(xiàn)有應(yīng)用程序的實(shí)例;如果是DELETED,則刪除現(xiàn)有應(yīng)用程序的實(shí)例,并且從應(yīng)用程序中找到所有的實(shí)例列表(實(shí)例狀態(tài)的狀態(tài)不僅是狀態(tài)是 UP,還有其他狀態(tài))如果實(shí)例列表為空,刪除應(yīng)用程序。
- 對(duì)提供的實(shí)例進(jìn)行洗牌,以便它們不總是以相同的順序返回。
三、服務(wù)發(fā)現(xiàn)
1、客戶端獲取服務(wù)實(shí)例
@Override
public List<ServiceInstance> getInstances(String serviceId) {
// 委托eurekaClient處理
List<InstanceInfo> infos = this.eurekaClient.getInstancesByVipAddress(serviceId,
false);
List<ServiceInstance> instances = new ArrayList<>();
for (InstanceInfo info : infos) {
instances.add(new EurekaServiceInstance(info));
}
return instances;
}跟Nacos的入口是類似的,需要實(shí)現(xiàn)spring-cloud-commons的DiscoveryClient接口。這里EurekaDiscoveryClient會(huì)委托Eureka項(xiàng)目里面的EurekaClient處理,見下面2分析。然后將Instances列表轉(zhuǎn)換為spring-cloud-commons里面的ServiceInstance類型列表。
2、從本地列表獲取
@Override
public List<InstanceInfo> getInstancesByVipAddress(String vipAddress, boolean secure) {
return getInstancesByVipAddress(vipAddress, secure, instanceRegionChecker.getLocalRegion());
}
@Override
public List<InstanceInfo> getInstancesByVipAddress(String vipAddress, boolean secure,
@Nullable String region) {
if (vipAddress == null) {
throw new IllegalArgumentException(
"Supplied VIP Address cannot be null");
}
Applications applications;
// 如果eureka:region:沒有指定,則使用默認(rèn)值且非空,即默認(rèn)使用默認(rèn)localRegion
if (instanceRegionChecker.isLocalRegion(region)) {
// 本地獲取
applications = this.localRegionApps.get();
} else {
applications = remoteRegionVsApps.get(region);
if (null == applications) {
logger.debug("No applications are defined for region {}, so returning an empty instance list for vip "
+ "address {}.", region, vipAddress);
return Collections.emptyList();
}
}
// secure默認(rèn)false
if (!secure) {
return applications.getInstancesByVirtualHostName(vipAddress);
} else {
return applications.getInstancesBySecureVirtualHostName(vipAddress);
}
}主要邏輯:
- 調(diào)用重載方法,vipAddress即serviceId,secure為false
- 如果eureka:region:沒有指定,則使用默認(rèn)值且非空,即默認(rèn)使用默認(rèn)localRegion。從本地應(yīng)用獲取列表。
- secure為false,獲取與虛擬主機(jī)名關(guān)聯(lián)的instance列表
到此這篇關(guān)于SpringCloud輪詢拉取注冊(cè)表與服務(wù)發(fā)現(xiàn)流程詳解的文章就介紹到這了,更多相關(guān)SpringCloud注冊(cè)表與服務(wù)發(fā)現(xiàn)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
java web實(shí)現(xiàn)郵箱發(fā)送功能
這篇文章主要為大家詳細(xì)介紹了java web實(shí)現(xiàn)郵箱發(fā)送功能,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-05-05
Java System.currentTimeMillis()時(shí)間的單位轉(zhuǎn)換與計(jì)算方式案例詳解
這篇文章主要介紹了Java System.currentTimeMillis()時(shí)間的單位轉(zhuǎn)換與計(jì)算方式案例詳解,本篇文章通過(guò)簡(jiǎn)要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下2021-08-08
SpringBoot+Quartz+數(shù)據(jù)庫(kù)存儲(chǔ)的完美集合
這篇文章主要介紹了SpringBoot+Quartz+數(shù)據(jù)庫(kù)存儲(chǔ)的示例代碼,本文通過(guò)實(shí)例代碼圖文相結(jié)合給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-02-02
關(guān)于Spring MVC同名參數(shù)綁定問題的解決方法
Spring MVC中的參數(shù)綁定還是蠻重要的,最近在使用中遇到了同名參數(shù)綁定的問題,想著總結(jié)分享出來(lái),下面這篇文章主要給大家介紹了關(guān)于Spring MVC同名參數(shù)綁定問題的解決方法,需要的朋友可以參考借鑒,下面來(lái)一起看看吧。2017-08-08
RocketMQ消息存儲(chǔ)文件的加載與恢復(fù)機(jī)制源碼分析
這篇文章主要介紹了RocketMQ源碼分析之消息存儲(chǔ)文件的加載與恢復(fù)機(jī)制詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-05-05
java實(shí)現(xiàn)文件和base64相互轉(zhuǎn)換
這篇文章主要為大家詳細(xì)介紹了java如何實(shí)現(xiàn)文件和base64相互轉(zhuǎn)換,文中的示例代碼講解詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下2023-11-11

