Eureka-Client獲取信息
啟動獲取
在客戶端應(yīng)用啟動時歪沃,初始化DiscoverClient的時候嗦锐,會主動去獲取一次注冊信息
@Inject
DiscoveryClient(ApplicationInfoManager applicationInfoManager, EurekaClientConfig config, AbstractDiscoveryClientOptionalArgs args,
Provider<BackupRegistry> backupRegistryProvider) {
// ...省略N多代碼
// 如果fetch-registry = true , 則去Eureka Server拉取注冊信息
if (clientConfig.shouldFetchRegistry() && !fetchRegistry(false)) {
// 如果所有的Eureka Server都不可用,那么從備用的服務(wù)里面去取數(shù)據(jù)
fetchRegistryFromBackup();
}
// ...省略N多代碼
// 設(shè)置定時器
initScheduledTasks();
}
shouldFetchRegistry : 默認true
fetchRegistry : 獲取注冊信息沪曙,此處傳入的是false奕污, 表面上看是不需要全量獲取,但是應(yīng)用第一次啟動的時候液走,
本地緩存為空碳默,所以還是會全量獲取的。
PS: 啟動時獲取注冊信息為全量缘眶。
定時器獲取
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);
}
// ...省略N多代碼
}
registryFetchIntervalSeconds : 默認值為30秒 嘱根,每30秒刷新一次、
定時器初始化巷懈,该抒,直接看CacheRefreshThread()
class CacheRefreshThread implements Runnable {
public void run() {
// 刷新注冊信息
refreshRegistry();
}
}
void refreshRegistry() {
try {
boolean isFetchingRemoteRegionRegistries = isFetchingRemoteRegionRegistries();
boolean remoteRegionsModified = false;
// 判斷是否需要全量獲取 , remoteRegionsModified 這個值來決定
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
synchronized (instanceRegionChecker.getAzToRegionMapper()) {
if (remoteRegionsToFetch.compareAndSet(currentRemoteRegions, latestRemoteRegions)) {
String[] remoteRegions = latestRemoteRegions.split(",");
remoteRegionsRef.set(remoteRegions);
instanceRegionChecker.getAzToRegionMapper().setRegionsToFetch(remoteRegions);
remoteRegionsModified = true;
} else {
logger.info("Remote regions to fetch modified concurrently," +
" ignoring change from {} to {}", currentRemoteRegions, latestRemoteRegions);
}
}
} else {
// Just refresh mapping to reflect any DNS/Property change
instanceRegionChecker.getAzToRegionMapper().refreshMapping();
}
}
// 獲取注冊信息
boolean success = fetchRegistry(remoteRegionsModified);
if (success) {
registrySize = localRegionApps.get().size();
lastSuccessfulRegistryFetchTimestamp = System.currentTimeMillis();
}
// 日志輸出 顶燕, 省略凑保。。
} catch (Throwable e) {
logger.error("Cannot fetch registry from server", e);
}
}
由上可以看到涌攻,系統(tǒng)在啟動的時候欧引,初始化了一個定時器,每30秒一次恳谎,用來刷新本地緩存信息芝此。
獲取注冊信息
private boolean fetchRegistry(boolean forceFullRegistryFetch) {
Stopwatch tracer = FETCH_REGISTRY_TIMER.start();
try {
// 獲取本地的緩存信息 , 也就是客戶端注冊信息
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("Disable delta property : {}", clientConfig.shouldDisableDelta());
logger.info("Single vip registry refresh property : {}", clientConfig.getRegistryRefreshSingleVipAddress());
logger.info("Force full registry fetch : {}", forceFullRegistryFetch);
logger.info("Application is null : {}", (applications == null));
logger.info("Registered Applications size is zero : {}",
(applications.getRegisteredApplications().size() == 0));
logger.info("Application version is -1: {}", (applications.getVersion() == -1));
getAndStoreFullRegistry();
} else {
// 增量獲取
getAndUpdateDelta(applications);
}
applications.setAppsHashCode(applications.getReconcileHashCode());
logTotalInstances();
} catch (Throwable e) {
logger.error(PREFIX + appPathIdentifier + " - was unable to refresh its cache! status = " + e.getMessage(), e);
return false;
} finally {
if (tracer != null) {
tracer.stop();
}
}
// 發(fā)布緩存刷新事件。
onCacheRefreshed();
// 更新本地應(yīng)用的狀態(tài)
updateInstanceRemoteStatus();
// registry was fetched successfully, so return true
return true;
}
clientConfig.shouldDisableDelta() : 是否禁用增量獲取癌蓖, 默認為false , 如果禁用了的話婚肆,那就只能是全量獲取了租副,總要獲取一下不是。
clientConfig.getRegistryRefreshSingleVipAddress() : 當(dāng)這個屬性不為空的時候较性,則全量獲取用僧。具體作用不是很清楚(苦笑)
forceFullRegistryFetch : 傳入的參數(shù),表示是否需要全量獲取
applications : 本地注冊信息的緩存赞咙,如果本地緩存為空责循,或者里面的版本號為-1,那么就需要全量獲取攀操,表示首次加載時院仿。
onCacheRefreshed() : 發(fā)布緩存刷新的事件,用戶可以自定義是否監(jiān)聽這個事件速和,比如需要將注冊信息的變化落庫歹垫。
全量獲取
private void getAndStoreFullRegistry() throws Throwable {
long currentUpdateGeneration = fetchRegistryGeneration.get();
logger.info("Getting all instance registry info from the eureka server");
Applications apps = null;
// 發(fā)送HTTP請求,去服務(wù)端獲取注冊信息
EurekaHttpResponse<Applications> httpResponse = clientConfig.getRegistryRefreshSingleVipAddress() == null
? eurekaTransport.queryClient.getApplications(remoteRegionsRef.get())
: eurekaTransport.queryClient.getVip(clientConfig.getRegistryRefreshSingleVipAddress(), remoteRegionsRef.get());
if (httpResponse.getStatusCode() == Status.OK.getStatusCode()) {
apps = httpResponse.getEntity();
}
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)) {
// 設(shè)置到本地緩存里面去
localRegionApps.set(this.filterAndShuffle(apps));
logger.debug("Got full registry with apps hashcode {}", apps.getAppsHashCode());
} else {
logger.warn("Not updating applications as another thread is updating it already");
}
}
在發(fā)送HTTP請求去服務(wù)端獲取注冊信息之前颠放,做了一個判斷排惨, 判斷registryRefreshSingleVipAddress是否為空, 這個字段
表示的意思是 “此客戶端只對一個單一的VIP注冊表的信息感興趣”碰凶,默認為null , 也就是說如果客戶端只對其中一個VIP 感興趣
那么就只獲取這一個暮芭, 否則全部獲取
this.filterAndShuffle(apps) : 是否需要過濾客戶端信息的狀態(tài),如果設(shè)置了eureka.shouldFilterOnlyUpInstances = true 這個屬性的話欲低,
客戶端獲取到注冊信息之后辕宏,會剔除非UP狀態(tài)的客戶端信息。
localRegionApps.set(this.filterAndShuffle(apps)) : 將注冊信息設(shè)置到本地內(nèi)存里面去伸头,使用AtomicReference類型做存儲匾效、
private final AtomicReference<Applications> localRegionApps = new AtomicReference<Applications>();
增量獲取
private void getAndUpdateDelta(Applications applications) throws Throwable {
long currentUpdateGeneration = fetchRegistryGeneration.get();
Applications delta = null;
// 增量獲取信息
EurekaHttpResponse<Applications> httpResponse = eurekaTransport.queryClient.getDelta(remoteRegionsRef.get());
if (httpResponse.getStatusCode() == Status.OK.getStatusCode()) {
delta = httpResponse.getEntity();
}
if (delta == null) {
// 增量獲取為空,則全量返回
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)) {
logger.debug("Got delta update with apps hashcode {}", delta.getAppsHashCode());
String reconcileHashCode = "";
//這里設(shè)置原子鎖的原因是怕某次調(diào)度網(wǎng)絡(luò)請求時間過長恤磷,導(dǎo)致同一時間有多線程拉取到增量信息并發(fā)修改
if (fetchRegistryUpdateLock.tryLock()) {
try {
// 將獲取到的增量信息和本地緩存信息合并面哼。
updateDelta(delta);
reconcileHashCode = getReconcileHashCode(applications);
} finally {
// 釋放鎖
fetchRegistryUpdateLock.unlock();
}
} else {
logger.warn("Cannot acquire update lock, aborting getAndUpdateDelta");
}
// ( HashCode 不一致|| 打印增量和全量的差異 )= true 重新去全量獲取
if (!reconcileHashCode.equals(delta.getAppsHashCode()) || clientConfig.shouldLogDeltaDiff()) {
reconcileAndLogDifference(delta, reconcileHashCode); // this makes a remoteCall
}
} 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());
}
}
步驟說明:
1.發(fā)起http請求,將服務(wù)端的客戶端變化的信息拉取過來扫步,如: register魔策, cancle, modify 有過這些操作的數(shù)據(jù)
2.上鎖,防止某次調(diào)度網(wǎng)絡(luò)請求時間過長河胎,導(dǎo)致同一時間有多線程拉取到增量信息并發(fā)修改
3.將請求過來的增量數(shù)據(jù)和本地的數(shù)據(jù)做合并
4.計算hashCode
5.如果hashCode不一致闯袒,或者clientConfig.shouldLogDeltaDiff() = true 的話,則又會去服務(wù)端發(fā)起一次全量獲取
合并數(shù)據(jù)
private void updateDelta(Applications delta) {
int deltaCount = 0;
// 循環(huán)拉取過來的應(yīng)用列表
for (Application app : delta.getRegisteredApplications()) {
// 循環(huán)這個應(yīng)用里面的實例(有多個實例代表是集群的。)
for (InstanceInfo instance : app.getInstances()) {
// 獲取本地的注冊應(yīng)用列表
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())) {// 添加事件
//根據(jù)AppName 獲取本地的數(shù)據(jù)政敢,看這個應(yīng)用是否存在
Application existingApp = applications.getRegisteredApplications(instance.getAppName());
if (existingApp == null) {
// 不存在其徙,則加到本地的應(yīng)用里面去
applications.addApplication(app);
}
logger.debug("Added instance {} to the existing apps in region {}", instance.getId(), instanceRegion);
// 為本地這個應(yīng)用添加這個實例
applications.getRegisteredApplications(instance.getAppName()).addInstance(instance);
} else if (ActionType.MODIFIED.equals(instance.getActionType())) { // 修改事件
//根據(jù)AppName 獲取本地的數(shù)據(jù),看這個應(yīng)用是否存在
Application existingApp = applications.getRegisteredApplications(instance.getAppName());
if (existingApp == null) {
// 不存在喷户,則加到本地的應(yīng)用里面去
applications.addApplication(app);
}
logger.debug("Modified instance {} to the existing apps ", instance.getId());
// 為本地這個應(yīng)用添加這個實例
applications.getRegisteredApplications(instance.getAppName()).addInstance(instance);
} else if (ActionType.DELETED.equals(instance.getActionType())) { // 刪除事件
Application existingApp = applications.getRegisteredApplications(instance.getAppName());
if (existingApp == null) {
applications.addApplication(app);
}
logger.debug("Deleted instance {} to the existing apps ", instance.getId());
// 移除這個實例
applications.getRegisteredApplications(instance.getAppName()).removeInstance(instance);
}
}
}
logger.debug("The total number of instances fetched by the delta processor : {}", deltaCount);
getApplications().setVersion(delta.getVersion());
getApplications().shuffleInstances(clientConfig.shouldFilterOnlyUpInstances());
for (Applications applications : remoteRegionVsApps.values()) {
applications.setVersion(delta.getVersion());
applications.shuffleInstances(clientConfig.shouldFilterOnlyUpInstances());
}
}
步驟說明:
1.從服務(wù)端獲取了最近這段時間唾那,新注冊新來的客戶端信息,有過修改的褪尝,被刪除的闹获, 這三大類的實例信息
然后通過覆蓋本地的數(shù)據(jù),移除數(shù)據(jù)河哑,來達到數(shù)據(jù)合并的需求避诽。
Eureka-Server接收請求
控制器接收請求
com.netflix.eureka.resources.ApplicationsResource , 程序入口
全量獲取
@GET
public Response getContainers(@PathParam("version") String version,
@HeaderParam(HEADER_ACCEPT) String acceptHeader,
@HeaderParam(HEADER_ACCEPT_ENCODING) String acceptEncoding,
@HeaderParam(EurekaAccept.HTTP_X_EUREKA_ACCEPT) String eurekaAccept,
@Context UriInfo uriInfo,
@Nullable @QueryParam("regions") String regionsStr) {
// 獲取注冊列表的區(qū)域
boolean isRemoteRegionRequested = null != regionsStr && !regionsStr.isEmpty();
String[] regions = null;
if (!isRemoteRegionRequested) {
EurekaMonitors.GET_ALL.increment();
} else {
regions = regionsStr.toLowerCase().split(",");
Arrays.sort(regions); // So we don't have different caches for same regions queried in different order.
EurekaMonitors.GET_ALL_WITH_REMOTE_REGIONS.increment();
}
// 判斷是否可以訪問
if (!registry.shouldAllowAccess(isRemoteRegionRequested)) {
return Response.status(Status.FORBIDDEN).build();
}
// 設(shè)置API版本
CurrentRequestVersion.set(Version.toEnum(version));
// 默認key的類型為JSON
KeyType keyType = Key.KeyType.JSON;
// 默認設(shè)置返回類型為JSON
String returnMediaType = MediaType.APPLICATION_JSON;
// 如果Accept為空,或者不包含JSON字符串(表示客戶端可能不接收JSON類型)璃谨,則設(shè)置返回XML類型的
if (acceptHeader == null || !acceptHeader.contains(HEADER_JSON_VALUE)) {
keyType = Key.KeyType.XML;
returnMediaType = MediaType.APPLICATION_XML;
}
// 構(gòu)建緩存KEY
Key cacheKey = new Key(Key.EntityType.Application,
ResponseCacheImpl.ALL_APPS,
keyType, CurrentRequestVersion.get(), EurekaAccept.fromString(eurekaAccept), regions
);
// 獲取緩存信息沙庐,返回給客戶端
Response response;
// 判斷請求接收類型是否是gzip ,如果是,則返回gzip的流出去
if (acceptEncoding != null && acceptEncoding.contains(HEADER_GZIP_VALUE)) {
response = Response.ok(responseCache.getGZIP(cacheKey))
.header(HEADER_CONTENT_ENCODING, HEADER_GZIP_VALUE)
.header(HEADER_CONTENT_TYPE, returnMediaType)
.build();
} else {
response = Response.ok(responseCache.get(cacheKey))
.build();
}
return response;
}
PS: 從Controller中可以看到佳吞,獲取注冊信息都是調(diào)用緩存操作類來最終獲取到的轨功,此處暫時先不講緩存機制的實現(xiàn)
后面會單獨開一篇文章來寫Eureka的緩存機制
獲取全量信息,主要就是從AbstractInstanceRegistry類getApplications中的registry中獲取的,代碼如下
public Applications getApplications() {
boolean disableTransparentFallback = serverConfig.disableTransparentFallbackToOtherRegion();
if (disableTransparentFallback) {
return getApplicationsFromLocalRegionOnly();
} else {
return getApplicationsFromAllRemoteRegions(); // Behavior of falling back to remote region can be disabled.
}
}
disableTransparentFallback : 官網(wǎng)解釋是 容达, 如果在遠程區(qū)域本地沒有實例運行古涧,對于應(yīng)用程序回退的舊行為是否被禁用, 默認為false花盐,所以此處僅
詳細講getApplicationsFromAllRemoteRegions() 羡滑;
public Applications getApplicationsFromAllRemoteRegions() {
return getApplicationsFromMultipleRegions(allKnownRemoteRegions);
}
public Applications getApplicationsFromMultipleRegions(String[] remoteRegions) {
boolean includeRemoteRegion = null != remoteRegions && remoteRegions.length != 0;
logger.debug("Fetching applications registry with remote regions: {}, Regions argument {}",
includeRemoteRegion, Arrays.toString(remoteRegions));
// 默認為false
if (includeRemoteRegion) {
GET_ALL_WITH_REMOTE_REGIONS_CACHE_MISS.increment();
} else {
GET_ALL_CACHE_MISS.increment();
}
Applications apps = new Applications();
apps.setVersion(1L);
// 循環(huán)該類中的CurrentHashMap, 這個MAP中,存儲的是所有的客戶端注冊的實例信息
// KEY 為客戶端的名稱算芯,value為客戶端的集群機器信息柒昏。
for (Entry<String, Map<String, Lease<InstanceInfo>>> entry : registry.entrySet()) {
Application app = null;
//
if (entry.getValue() != null) {
// 獲取Lease信息,里面有每個實例的instance信息熙揍,分裝成Application實體
for (Entry<String, Lease<InstanceInfo>> stringLeaseEntry : entry.getValue().entrySet()) {
Lease<InstanceInfo> lease = stringLeaseEntry.getValue();
if (app == null) {
app = new Application(lease.getHolder().getAppName());
}
app.addInstance(decorateInstanceInfo(lease));
}
}
if (app != null) {
//放入 Applications里面去
apps.addApplication(app);
}
}
// 职祷。。届囚。有梆。省略N多代碼
apps.setAppsHashCode(apps.getReconcileHashCode());
return apps;
}
總結(jié): 從上面的代碼上來看,全量獲取的機制很簡單意系,主要是把服務(wù)端本地的CurrentHashMap里面存儲的客戶端信息泥耀,封裝成
Application實體,然后返回蛔添。
增量獲取
@Path("delta")
@GET
public Response getContainerDifferential(
@PathParam("version") String version,
@HeaderParam(HEADER_ACCEPT) String acceptHeader,
@HeaderParam(HEADER_ACCEPT_ENCODING) String acceptEncoding,
@HeaderParam(EurekaAccept.HTTP_X_EUREKA_ACCEPT) String eurekaAccept,
@Context UriInfo uriInfo, @Nullable @QueryParam("regions") String regionsStr) {
// ..... 省略N多代碼
Key cacheKey = new Key(Key.EntityType.Application,
ResponseCacheImpl.ALL_APPS_DELTA,
keyType, CurrentRequestVersion.get(), EurekaAccept.fromString(eurekaAccept), regions
);
// ..... 省略N多代碼
if (acceptEncoding != null
&& acceptEncoding.contains(HEADER_GZIP_VALUE)) {
return Response.ok(responseCache.getGZIP(cacheKey))
.header(HEADER_CONTENT_ENCODING, HEADER_GZIP_VALUE)
.header(HEADER_CONTENT_TYPE, returnMediaType)
.build();
} else {
return Response.ok(responseCache.get(cacheKey))
.build();
}
}
獲取增量信息的代碼在AbstractInstanceRegistry類中痰催,代碼如下
public Applications getApplicationDeltas() {
GET_ALL_CACHE_MISS_DELTA.increment();
// 最近變化過的應(yīng)用兜辞,初始化一個實體
Applications apps = new Applications();
// 增量獲取的版本號
apps.setVersion(responseCache.getVersionDelta().get());
Map<String, Application> applicationInstancesMap = new HashMap<String, Application>();
try {
// 上寫鎖
write.lock();
// 最近產(chǎn)生過變化的客戶端,都在這個隊列里面
Iterator<RecentlyChangedItem> iter = this.recentlyChangedQueue.iterator();
logger.debug("The number of elements in the delta queue is :"
+ this.recentlyChangedQueue.size());
// 循環(huán)隊列
while (iter.hasNext()) {
// 獲取隊列中的lease信息夸溶,這里面封裝的就是客戶端的實例信息
Lease<InstanceInfo> lease = iter.next().getLeaseInfo();
InstanceInfo instanceInfo = lease.getHolder();
Object[] args = {instanceInfo.getId(),
instanceInfo.getStatus().name(),
instanceInfo.getActionType().name()};
logger.debug(
"The instance id %s is found with status %s and actiontype %s",
args);
Application app = applicationInstancesMap.get(instanceInfo
.getAppName());
if (app == null) {
// 組裝成一個Application實體逸吵,同時放入Applications里面去
app = new Application(instanceInfo.getAppName());
applicationInstancesMap.put(instanceInfo.getAppName(), app);
apps.addApplication(app);
}
app.addInstance(decorateInstanceInfo(lease));
}
boolean disableTransparentFallback = serverConfig.disableTransparentFallbackToOtherRegion();
// 暫時沒看明白這里的作用(苦笑。缝裁。)
if (!disableTransparentFallback) {
Applications allAppsInLocalRegion = getApplications(false);
for (RemoteRegionRegistry remoteRegistry : this.regionNameVSRemoteRegistry.values()) {
Applications applications = remoteRegistry.getApplicationDeltas();
for (Application application : applications.getRegisteredApplications()) {
Application appInLocalRegistry =
allAppsInLocalRegion.getRegisteredApplications(application.getName());
if (appInLocalRegistry == null) {
apps.addApplication(application);
}
}
}
}
// 獲取全量的注冊信息
Applications allApps = getApplications(!disableTransparentFallback);
// 設(shè)置HashCode
apps.setAppsHashCode(allApps.getReconcileHashCode());
return apps;
} finally {
write.unlock();
}
}
上面主要用到了一個租約變化的隊列胁塞, 這里面在客戶端發(fā)生變化時,都會在這里面加入一條信息压语, 如: 注冊,下線编检,過期
等操作胎食,租約變化隊列里面的數(shù)據(jù)默認保存3分鐘,會有一個定時器沒30秒清理一次允懂。
retentionTimeInMSInDeltaQueue : 客戶端保持增量信息緩存的時間厕怜,從而保證不會丟失這些信息,單位為毫秒蕾总,默認為3 * 60 * 1000
private TimerTask getDeltaRetentionTask() {
return new TimerTask() {
@Override
public void run() {
Iterator<RecentlyChangedItem> it = recentlyChangedQueue.iterator();
while (it.hasNext()) {
// 最后更新時間小于當(dāng)前時間-3分鐘粥航,那么就會被移除
if (it.next().getLastUpdateTime() <
System.currentTimeMillis() - serverConfig.getRetentionTimeInMSInDeltaQueue()) {
it.remove();
} else {
break;
}
}
}
};
}
獲取到了這些變化的客戶端信息,返回Eureka Clien 之后生百,通過集合合并递雀,就可以得到最新的緩存數(shù)據(jù)了。
對于服務(wù)端來說蚀浆, 接收全量獲取和增量獲取的請求缀程,區(qū)別在于,構(gòu)成的KEY不同市俊, 全量獲取的KEY 為ALL_APPS杨凑,
增量獲取的KEY是ALL_APPS_DELTA , 然后都是通過緩存操作類去獲取數(shù)據(jù)摆昧,因此最重要的是緩存類的功能實現(xiàn)
接下來會單獨開一篇講Eureka Server 服務(wù)端的緩存機制撩满。