Spring中的Eureka服務(wù)過期詳細解析
Eureka 過期
如果有一些服務(wù)過期了,或者宕機了,就不會調(diào)用shutdown()方法,也不會去發(fā)送請求下線服務(wù)實例。
eureka就專門實現(xiàn)了一套過期的策略,去下線一些過期的服務(wù)。
它的入口就是在eureka server在啟動初始化的時候,registry.openForTraffic(applicationInfoManager, registryCount);。
于是就點進方法,下面為它的代碼
@Override public void openForTraffic(ApplicationInfoManager applicationInfoManager, int count) { // Renewals happen every 30 seconds and for a minute it should be a factor of 2. this.expectedNumberOfClientsSendingRenews = count; updateRenewsPerMinThreshold(); logger.info("Got {} instances from neighboring DS node", count); logger.info("Renew threshold is: {}", numberOfRenewsPerMinThreshold); this.startupTime = System.currentTimeMillis(); if (count > 0) { this.peerInstancesTransferEmptyOnStartup = false; } DataCenterInfo.Name selfName = applicationInfoManager.getInfo().getDataCenterInfo().getName(); boolean isAws = Name.Amazon == selfName; if (isAws && serverConfig.shouldPrimeAwsReplicaConnections()) { logger.info("Priming AWS connections for all replicas.."); primeAwsReplicas(applicationInfoManager); } logger.info("Changing status to UP"); applicationInfoManager.setInstanceStatus(InstanceStatus.UP); super.postInit(); }
expectedNumberOfClientsSendingRenews為從注冊表獲取的注冊服務(wù)實例的數(shù)量。
protected void updateRenewsPerMinThreshold() { this.numberOfRenewsPerMinThreshold = (int) (this.expectedNumberOfClientsSendingRenews * (60.0 / serverConfig.getExpectedClientRenewalIntervalSeconds()) * serverConfig.getRenewalPercentThreshold()); }
getExpectedClientRenewalIntervalSeconds 默認為30秒,getRenewalPercentThreshold 是檢測的一個系數(shù)為85%,這個公式會算出期望的存活的數(shù)量的。如果注冊的數(shù)量為20個* 2 * 0.85 =34,這個是在自我保護機制中進行對比的,判斷是否進入保護機制,進入的話,則不能摘除服務(wù),不會走下面的方法 。下面一段代碼都不是太重要,當(dāng)來到super.postInit();的時候,顧名思義,是要做一些初始化的事情。下面為詳細代碼:
protected void postInit() { renewsLastMin.start(); if (evictionTaskRef.get() != null) { evictionTaskRef.get().cancel(); } evictionTaskRef.set(new EvictionTask()); evictionTimer.schedule(evictionTaskRef.get(), serverConfig.getEvictionIntervalTimerInMs(), serverConfig.getEvictionIntervalTimerInMs()); }
renewsLastMin.start();看上去是要開啟一個線程,看名字是續(xù)租最后一分鐘,代碼設(shè)計的還是很巧妙的,設(shè)置出2個Bucket,一個專門存當(dāng)前時間,一個存一分鐘之前的時間,這個線程1分鐘運行一次,這樣就可以拿到一分鐘之前的時間了。
private final long sampleInterval; //1分鐘 public synchronized void start() { if (!isActive) { timer.schedule(new TimerTask() { @Override public void run() { try { // Zero out the current bucket. //每分鐘這塊調(diào)度異常 //currentBucket 是用來更新當(dāng)前這一分鐘的次數(shù)的 //lastBucket 是保留了上一分鐘的心跳次數(shù) //timer調(diào)度任務(wù),1分鐘來一次,就將上一分鐘的心跳次數(shù)設(shè)置到lastBucket中去 lastBucket.set(currentBucket.getAndSet(0)); } catch (Throwable e) { logger.error("Cannot reset the Measured Rate", e); } } }, sampleInterval, sampleInterval); isActive = true; } }
evictionTaskRef.set(new EvictionTask());這里會new一個task,下面就看一下,它到底做了什么,compensation的意思是補償,那這個compensationTimeMs就是補償時間(毫秒)。在getCompensationTimeMs這個方法中,會用當(dāng)前時間減去上一次執(zhí)行的時間,在減去摘除間隔時間(60s),在判斷這個時間是否大于0,如果大于0,這說明延遲了,則會得到一個補償時間,這個時間很關(guān)鍵,因為后面算服務(wù)實例是否過期要下線的時候,也和它有關(guān)系。
/* visible for testing */ class EvictionTask extends TimerTask { private final AtomicLong lastExecutionNanosRef = new AtomicLong(0l); @Override public void run() { try { long compensationTimeMs = getCompensationTimeMs(); logger.info("Running the evict task with compensationTime {}ms", compensationTimeMs); evict(compensationTimeMs); } catch (Throwable e) { logger.error("Could not run the evict task", e); } } /** * compute a compensation time defined as the actual time this task was executed since the prev iteration, * vs the configured amount of time for execution. This is useful for cases where changes in time (due to * clock skew or gc for example) causes the actual eviction task to execute later than the desired time * according to the configured cycle. */ //比預(yù)期的時間晚 long getCompensationTimeMs() { //先獲取當(dāng)前時間 long currNanos = getCurrentTimeNano(); //上一次這個EvictionTask被執(zhí)行的時間。 long lastNanos = lastExecutionNanosRef.getAndSet(currNanos); if (lastNanos == 0l) { return 0l; } long elapsedMs = TimeUnit.NANOSECONDS.toMillis(currNanos - lastNanos); long compensationTime = elapsedMs - serverConfig.getEvictionIntervalTimerInMs(); return compensationTime <= 0l ? 0l : compensationTime; } long getCurrentTimeNano() { // for testing return System.nanoTime(); } }
接著就把補償時間帶到摘除的方法中,代碼如下:
public void evict(long additionalLeaseMs) { logger.debug("Running the evict task"); //是否運行主動刪除故障的實例,自我保護機制有關(guān) if (!isLeaseExpirationEnabled()) { logger.debug("DS: lease expiration is currently disabled."); return; } // We collect first all expired items, to evict them in random order. For large eviction sets, // if we do not that, we might wipe out whole apps before self preservation kicks in. By randomizing it, // the impact should be evenly distributed across all applications. List<Lease<InstanceInfo>> expiredLeases = new ArrayList<>(); for (Entry<String, Map<String, Lease<InstanceInfo>>> groupEntry : registry.entrySet()) { Map<String, Lease<InstanceInfo>> leaseMap = groupEntry.getValue(); if (leaseMap != null) { for (Entry<String, Lease<InstanceInfo>> leaseEntry : leaseMap.entrySet()) { Lease<InstanceInfo> lease = leaseEntry.getValue(); //如果超過3分鐘加上補償時間,就認為故障了 if (lease.isExpired(additionalLeaseMs) && lease.getHolder() != null) { expiredLeases.add(lease); } } } } // To compensate for GC pauses or drifting local time, we need to use current registry size as a base for // triggering self-preservation. Without that we would wipe out full registry. //20 int registrySize = (int) getLocalRegistrySize(); // 20*0.85 int registrySizeThreshold = (int) (registrySize * serverConfig.getRenewalPercentThreshold()); //3 int evictionLimit = registrySize - registrySizeThreshold; int toEvict = Math.min(expiredLeases.size(), evictionLimit); if (toEvict > 0) { logger.info("Evicting {} items (expired={}, evictionLimit={})", toEvict, expiredLeases.size(), evictionLimit); Random random = new Random(System.currentTimeMillis()); //隨機挑選3個服務(wù) for (int i = 0; i < toEvict; i++) { // Pick a random item (Knuth shuffle algorithm) int next = i + random.nextInt(expiredLeases.size() - i); Collections.swap(expiredLeases, i, next); Lease<InstanceInfo> lease = expiredLeases.get(i); String appName = lease.getHolder().getAppName(); String id = lease.getHolder().getId(); EXPIRED.increment(); logger.warn("DS: Registry: expired lease for {}/{}", appName, id); //對這個隨機挑選出來的,這個也是shutdown方法調(diào)用的 internalCancel(appName, id, false); } } }
isLeaseExpirationEnabled()這個方法,主要就是自我保護,前面已經(jīng)說過了,此處就不在介紹了。
接著會遍歷拿到所有的服務(wù)實例,里面包含它的續(xù)租時間,會判斷它有沒有過期,過期的話就會加到要摘除的集合中。公式就是當(dāng)前時間是否大約最后一次修改的時候加上續(xù)租時間和補償時間,大于的話就是過期了。
public boolean isExpired(long additionalLeaseMs) { //當(dāng)前時間是否大于上一次心跳時間加上90s+再加上補償時間 return (evictionTimestamp > 0 || System.currentTimeMillis() > (lastUpdateTimestamp + duration + additionalLeaseMs)); }
這塊代碼中,會計算出一個最大的摘除的個數(shù),如果是20個的話,最大摘除3個。因此最后會選擇這3和故障的6個選擇一個最小的,進行隨機摘除,然后調(diào)用下線的方法。
到此這篇關(guān)于Spring中的Eureka服務(wù)過期詳細解析的文章就介紹到這了,更多相關(guān)Eureka服務(wù)過期內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- SpringCloud啟動eureka server后,沒報錯卻不能訪問管理頁面(404問題)
- 解決Error:(5,55)java:程序包org.springframework.cloud.netflix.eureka.server不存在問題
- SpringCloud集成Eureka并實現(xiàn)負載均衡的過程詳解
- SpringCloud中的Eureka注冊中心詳細解讀
- springboot?去掉netflix?禁用Eureka的解決方法
- SpringCloud中的Eureka集群配置方法
- Spring Cloud Nacos 和 Eureka區(qū)別解析
- Spring?Cloud?Eureka高可用配置(踩坑記錄)
相關(guān)文章
Java Swing SpringLayout彈性布局的實現(xiàn)代碼
這篇文章主要介紹了Java Swing SpringLayout彈性布局的實現(xiàn)代碼,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-12-12DynamicDataSource怎樣解決多數(shù)據(jù)源的事務(wù)問題
這篇文章主要介紹了DynamicDataSource怎樣解決多數(shù)據(jù)源的事務(wù)問題,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2023-07-07使用Java和Selenium實現(xiàn)滑塊驗證的自動化登錄功能
在現(xiàn)代Web應(yīng)用中,滑塊驗證碼被廣泛用于防止自動化腳本的濫用,滑塊驗證通常要求用戶通過拖動滑塊來完成驗證,然而,在某些場景下,如自動化測試或批量登錄,我們需要通過編程手段解決滑塊驗證問題,本文將詳細介紹如何使用Java和Selenium實現(xiàn)滑塊驗證的自動化登錄2025-01-01springAI結(jié)合ollama簡單實現(xiàn)小結(jié)
本文主要介紹了springAI結(jié)合ollama簡單實現(xiàn),文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2025-03-03Java統(tǒng)計50個10到50之間整數(shù)的隨機出現(xiàn)次數(shù)
這篇文章主要為大家詳細介紹了Java統(tǒng)計50個10到50之間整數(shù)的隨機出現(xiàn)次數(shù),具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-07-07C#使用MySQLConnectorNet和MySQLDriverCS操作MySQL的方法
這篇文章主要介紹了C#使用MySQLConnectorNet和MySQLDriverCS操作MySQL的方法,相比普通方法能夠在Windows下簡化很多操作步驟,需要的朋友可以參考下2016-04-04