<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
- eureka版本:1.10.11
- Spring Cloud : 2020.0.2
- Spring Boot :2.4.4
測試程式碼:github.com/hsfxuebao/s…
服務離線,即某服務不能對外提供服務了。服務離線的原因有兩種:服務下架與服務下線。
提交如下POST請求
,可實現相應的服務離線操作:
{ "status":"OUT_OF_SERVICE" 或 "UP" }
注意,從Spring Cloud 2020.0.0版本開始,服務平滑上下線的監控終端由service-registry變更為 了serviceregistry
可以通過直接向Eureka Server
提交不同的請求的方式來實現指定服務離線操作:
服務下架:通過向eureka server
傳送DELETE請求
來刪除指定client
的服務
http://${server}:${port}/eureka/apps/${serviceName}/${instanceId}
服務下線:通過向eureka server
傳送PUT請求
來修改指定client的status
,其中${value}
的取值 為:OUT_OF_SERVICE或UP
http://${server}:${port}/eureka/apps/${serviceName}/${instanceId}/stat us?value=${value}
CANCEL_OVERRIDE
使用者提交的狀態修改請求中指定的狀態,除了InstanceInfo
的內建列舉類InstanceStatus
中定義的狀態 外,還可以是CANCEL_OVERRIDE狀態
。
若使用者提交的狀態為CANCEL_OVERRIDE
,則Client會通過Jersey
向Server提交一個DELETE請求
,用於 在Server端將對應InstanceInfo
的overridenStatus
修改為UNKNWON
,即刪除了原來的overridenStatus
的狀態值。此時,該Client傳送的心跳Server是不接收的。Server會向該Client返回404
。
public class EurekaClientAutoConfiguration { @Configuration(proxyBeanMethods = false) @ConditionalOnRefreshScope protected static class RefreshableEurekaClientConfiguration { @Bean(destroyMethod = "shutdown") @ConditionalOnMissingBean(value = EurekaClient.class, search = SearchStrategy.CURRENT) @org.springframework.cloud.context.config.annotation.RefreshScope @Lazy public EurekaClient eurekaClient(ApplicationInfoManager manager, EurekaClientConfig config, EurekaInstanceConfig instance, @Autowired(required = false) HealthCheckHandler healthCheckHandler) { } } }
當Actuator
監聽到服務下架時,會呼叫DiscoveryClient.shutdown()
方法:
// 服務下架 @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()); } // todo 取消定時任務 cancelScheduledTasks(); // If APPINFO was registered if (applicationInfoManager != null && clientConfig.shouldRegisterWithEureka() && clientConfig.shouldUnregisterOnShutdown()) { applicationInfoManager.setInstanceStatus(InstanceStatus.DOWN); // todo 服務下架 unregister(); } if (eurekaTransport != null) { eurekaTransport.shutdown(); } heartbeatStalenessMonitor.shutdown(); registryStalenessMonitor.shutdown(); Monitors.unregisterObject(this); logger.info("Completed shut down of DiscoveryClient"); } }
有兩個核心方法,我們分別看一下。
取消定時任務。
private void cancelScheduledTasks() { if (instanceInfoReplicator != null) { instanceInfoReplicator.stop(); } if (heartbeatExecutor != null) { heartbeatExecutor.shutdownNow(); } if (cacheRefreshExecutor != null) { cacheRefreshExecutor.shutdownNow(); } if (scheduler != null) { scheduler.shutdownNow(); } if (cacheRefreshTask != null) { cacheRefreshTask.cancel(); } if (heartbeatTask != null) { heartbeatTask.cancel(); } }
傳送服務下架請求。
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 + "{} - deregister status: {}", appPathIdentifier, httpResponse.getStatusCode()); } catch (Exception e) { logger.error(PREFIX + "{} - de-registration failed{}", appPathIdentifier, e.getMessage(), e); } } }
@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); // delete 請求 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(); } } }
服務下架請求:DELETE請求,path:"apps/" + appName + '/' + id;
Eureka 整合了 Actuator
,可以通過 Actuator
變更範例在伺服器端的狀態。spring cloud整合eureka,入口在 spring-cloud-common
下的spring.factories
:
@Configuration(proxyBeanMethods = false) public class ServiceRegistryAutoConfiguration { @ConditionalOnBean(ServiceRegistry.class) @ConditionalOnClass(Endpoint.class) protected class ServiceRegistryEndpointConfiguration { @Autowired(required = false) private Registration registration; @Bean @ConditionalOnAvailableEndpoint public ServiceRegistryEndpoint serviceRegistryEndpoint(ServiceRegistry serviceRegistry) { ServiceRegistryEndpoint endpoint = new ServiceRegistryEndpoint(serviceRegistry); endpoint.setRegistration(this.registration); return endpoint; } } }
ServiceRegistryAutoConfiguration
是一個設定類,往容器中注入ServiceRegistryEndpoint
:
@Endpoint(id = "serviceregistry") public class ServiceRegistryEndpoint { ... @WriteOperation public ResponseEntity<?> setStatus(String status) { Assert.notNull(status, "status may not by null"); if (this.registration == null) { return ResponseEntity.status(HttpStatus.NOT_FOUND).body("no registration found"); } // 變更狀態 this.serviceRegistry.setStatus(this.registration, status); return ResponseEntity.ok().build(); } @ReadOperation public ResponseEntity getStatus() { if (this.registration == null) { return ResponseEntity.status(HttpStatus.NOT_FOUND).body("no registration found"); } // 獲取狀態 return ResponseEntity.ok().body(this.serviceRegistry.getStatus(this.registration)); } }
核心方法ServiceRegistry#setStatus
:
@Override public void setStatus(EurekaRegistration registration, String status) { // 獲取範例資訊 InstanceInfo info = registration.getApplicationInfoManager().getInfo(); // TODO: howto deal with delete properly? if ("CANCEL_OVERRIDE".equalsIgnoreCase(status)) { // 如果變更狀態請求傳過來 status = "CANCEL_OVERRIDE",向伺服器端發起 Jersey 刪除狀態請求 registration.getEurekaClient().cancelOverrideStatus(info); return; } // TODO: howto deal with status types across discovery systems? InstanceInfo.InstanceStatus newStatus = InstanceInfo.InstanceStatus.toEnum(status); // 如果不是刪除狀態,則向伺服器端發起 Jersey 變更狀態請求 registration.getEurekaClient().setStatus(newStatus, info); }
核心流程有2個,分別為
status
為CANCEL_OVERRIDE
:
public void cancelOverrideStatus(InstanceInfo info) { getEurekaHttpClient().deleteStatusOverride(info.getAppName(), info.getId(), info); } @Override public EurekaHttpResponse<Void> deleteStatusOverride(String appName, String id, InstanceInfo info) { String urlPath = "apps/" + appName + '/' + id + "/status"; ClientResponse response = null; try { Builder requestBuilder = jerseyClient.resource(serviceUrl) .path(urlPath) .queryParam("lastDirtyTimestamp", info.getLastDirtyTimestamp().toString()) .getRequestBuilder(); addExtraHeaders(requestBuilder); // DELETE 請求 response = requestBuilder.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(); } } }
刪除deleteStatusOverride請求: DELETE請求 path:"apps/" + appName + '/' + id + "/status"
直接呼叫setStatus()
方法:
@Override public EurekaHttpResponse<Void> statusUpdate(String appName, String id, InstanceStatus newStatus, InstanceInfo info) { String urlPath = "apps/" + appName + '/' + id + "/status"; ClientResponse response = null; try { Builder requestBuilder = jerseyClient.resource(serviceUrl) .path(urlPath) .queryParam("value", newStatus.name()) .queryParam("lastDirtyTimestamp", info.getLastDirtyTimestamp().toString()) .getRequestBuilder(); addExtraHeaders(requestBuilder); // PUT 請求 response = requestBuilder.put(ClientResponse.class); return anEurekaHttpResponse(response.getStatus()).headers(headersOf(response)).build(); } finally { if (logger.isDebugEnabled()) { logger.debug("Jersey HTTP PUT {}/{}; statusCode={}", serviceUrl, urlPath, response == null ? "N/A" : response.getStatus()); } if (response != null) { response.close(); } } }
變更狀態請求:PUT請求,path為 :"apps/" + appName + '/' + id + "/status"
// EurekaServiceRegistry.class public Object getStatus(EurekaRegistration registration) { String appname = registration.getApplicationInfoManager().getInfo().getAppName(); String instanceId = registration.getApplicationInfoManager().getInfo().getId(); // 獲取本地範例資訊 InstanceInfo info = registration.getEurekaClient().getInstanceInfo(appname, instanceId); HashMap<String, Object> status = new HashMap<>(); if (info != null) { // 從範例資訊取出相應狀態返回 status.put("status", info.getStatus().toString()); status.put("overriddenStatus", info.getOverriddenStatus().toString()); } else { // 如果範例資訊不存在,則返回 UNKNOWN 狀態 status.put("status", UNKNOWN.toString()); } return status; }
參考文章
springcloud-source-study學習github地址
以上就是Eureka原始碼解析服務離線狀態變更的詳細內容,更多關於Eureka 服務離線狀態變更的資料請關注it145.com其它相關文章!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45