Spring中Eureka的服務(wù)下線詳解
Eureka 下線
// DiscoveryClient.java
@PreDestroy
@Override
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()) {
//講服務(wù)實例的狀態(tài)設(shè)置為DOWN
applicationInfoManager.setInstanceStatus(InstanceStatus.DOWN);
//取消注冊
unregister();
}
//關(guān)閉網(wǎng)絡(luò)通信組件
if (eurekaTransport != null) {
eurekaTransport.shutdown();
}
//關(guān)閉監(jiān)聽器
heartbeatStalenessMonitor.shutdown();
registryStalenessMonitor.shutdown();
Monitors.unregisterObject(this);
logger.info("Completed shut down of DiscoveryClient");
}
}- 調(diào)用 ApplicationInfoManager#setInstanceStatus(...) 方法,設(shè)置應(yīng)用實例為關(guān)閉( DOWN )。
- 調(diào)用 #unregister() 方法,實現(xiàn)代碼如下:
// DiscoveryClient.java
void unregister() {
// It can be null if shouldRegisterWithEureka == false
if(eurekaTransport != null && eurekaTransport.registrationClient != null) {
try {
logger.info("Unregistering ...");
EurekaHttpResponse<Void> httpResponse = eurekaTransport.registrationClient.cancel(instanceInfo.getAppName(), instanceInfo.getId());
logger.info(PREFIX + appPathIdentifier + " - deregister status: " + httpResponse.getStatusCode());
} catch (Exception e) {
logger.error(PREFIX + appPathIdentifier + " - de-registration failed" + e.getMessage(), e);
}
}
}
// AbstractJerseyEurekaHttpClient.java
@Override
public EurekaHttpResponse<Void> cancel(String appName, String id) {
String urlPath = "apps/" + appName + '/' + id;
ClientResponse response = null;
try {
Builder resourceBuilder = jerseyClient.resource(serviceUrl).path(urlPath).getRequestBuilder();
addExtraHeaders(resourceBuilder);
response = resourceBuilder.delete(ClientResponse.class);
return anEurekaHttpResponse(response.getStatus()).headers(headersOf(response)).build();
} finally {
if (logger.isDebugEnabled()) {
logger.debug("Jersey HTTP DELETE {}/{}; statusCode={}", serviceUrl, urlPath, response == null ? "N/A" : response.getStatus());
}
if (response != null) {
response.close();
}
}
}- 調(diào)用 AbstractJerseyEurekaHttpClient#cancel(...) 方法,DELETE 請求 Eureka-Server 的 apps/${APP_NAME}/${INSTANCE_INFO_ID} 接口,實現(xiàn)應(yīng)用實例信息的下線。
com.netflix.eureka.resources.InstanceResource,處理單個應(yīng)用實例信息的請求操作的 Resource ( Controller )。
下線應(yīng)用實例信息的請求,映射 InstanceResource#cancelLease() 方法,實現(xiàn)代碼如下:
@DELETE
public Response cancelLease(
@HeaderParam(PeerEurekaNode.HEADER_REPLICATION) String isReplication) {
// 下線
boolean isSuccess = registry.cancel(app.getName(), id, "true".equals(isReplication));
if (isSuccess) { // 下線成功
logger.debug("Found (Cancel): " + app.getName() + " - " + id);
return Response.ok().build();
} else { // 下線成功
logger.info("Not Found (Cancel): " + app.getName() + " - " + id);
return Response.status(Status.NOT_FOUND).build();
}
}調(diào)用 AbstractInstanceRegistry#cancel(...) 方法,下線應(yīng)用實例信息,實現(xiàn)代碼如下:
@Override
public boolean cancel(String appName, String id, boolean isReplication) {
return internalCancel(appName, id, isReplication);
}
/**
* {@link #cancel(String, String, boolean)} method is overridden by {@link PeerAwareInstanceRegistry}, so each
* cancel request is replicated to the peers. This is however not desired for expires which would be counted
* in the remote peers as valid cancellations, so self preservation mode would not kick-in.
*/
protected boolean internalCancel(String appName, String id, boolean isReplication) {
read.lock();
try {
CANCEL.increment(isReplication);
Map<String, Lease<InstanceInfo>> gMap = registry.get(appName);
Lease<InstanceInfo> leaseToCancel = null;
if (gMap != null) {
leaseToCancel = gMap.remove(id);
}
//將服務(wù)實例加入最近下線的queue中
recentCanceledQueue.add(new Pair<Long, String>(System.currentTimeMillis(), appName + "(" + id + ")"));
InstanceStatus instanceStatus = overriddenInstanceStatusMap.remove(id);
if (instanceStatus != null) {
logger.debug("Removed instance id {} from the overridden map which has value {}", id, instanceStatus.name());
}
if (leaseToCancel == null) {
CANCEL_NOT_FOUND.increment(isReplication);
logger.warn("DS: Registry: cancel failed because Lease is not registered for: {}/{}", appName, id);
return false;
} else {
//調(diào)用cancel,保存一個時間戳
leaseToCancel.cancel();
InstanceInfo instanceInfo = leaseToCancel.getHolder();
String vip = null;
String svip = null;
if (instanceInfo != null) {
instanceInfo.setActionType(ActionType.DELETED);
//丟到最近改變的隊列中
recentlyChangedQueue.add(new RecentlyChangedItem(leaseToCancel));
//設(shè)置了最近一次變更的時間戳
instanceInfo.setLastUpdatedTimestamp();
vip = instanceInfo.getVIPAddress();
svip = instanceInfo.getSecureVipAddress();
//服務(wù)的注冊,下線,過期,都會代表這個服務(wù)實例變化了,都會將自己放入最近改變的隊列中
//這個最近改變的隊列,只會保留最近3分鐘的實例
//所以說eureka client拉取增量注冊表的時候,其實就是拉取最近3分鐘有變化的服務(wù)實例。
}
invalidateCache(appName, vip, svip);
logger.info("Cancelled instance {}/{} (replication={})", appName, id, isReplication);
}
} finally {
read.unlock();
}
synchronized (lock) {
if (this.expectedNumberOfClientsSendingRenews > 0) {
// Since the client wants to cancel it, reduce the number of clients to send renews.
//修改期望值
this.expectedNumberOfClientsSendingRenews = this.expectedNumberOfClientsSendingRenews - 1;
updateRenewsPerMinThreshold();
}
}
return true;
}到此這篇關(guān)于Spring中Eureka的服務(wù)下線詳解的文章就介紹到這了,更多相關(guān)Eureka的服務(wù)下線內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
詳解MyBatis resultType與resultMap中的幾種返回類型
本文主要介紹了MyBatis resultType與resultMap中的幾種返回類型,文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2021-09-09
Springboot?接口需要接收參數(shù)類型是數(shù)組問題
這篇文章主要介紹了Springboot?接口需要接收參數(shù)類型是數(shù)組問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-01-01
Intellij IDEA基于Springboot的遠程調(diào)試(圖文)
這篇文章主要介紹了Intellij IDEA基于Springboot的遠程調(diào)試(圖文),文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2019-10-10
SpringBoot從Nacos讀取MySQL數(shù)據(jù)庫配置錯誤:Public Key Retrieva
最近的項目,突然都從MySQL5.7升級到8.0了,有些項目能運行成功,有些項目遇到了問題,啟動不成功,顯示數(shù)據(jù)庫方面的異常信息,本文給大家介紹了SpringBoot從Nacos讀取MySQL數(shù)據(jù)庫配置錯誤:Public Key Retrieval is not allowed的解決方案,需要的朋友可以參考下2024-04-04
java題解Leetcode 8字符串轉(zhuǎn)換整數(shù)
這篇文章主要為大家介紹了java題解Leetcode 8字符串轉(zhuǎn)換整數(shù)實現(xiàn),有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進步,早日升職加薪2023-06-06

