概述
背景
服务的提供者下线时,调用者依然会发送请求到下线的服务提供者实例上,导致请求失败影响用户体验
解决思路
服务提供者下线时,及时通知调用者,从负载均衡列表中删除下线的实例
前提知识
EurekaServer 端 应用列表有两份,都是使用 Map 来维护,其中
ReadOnlyMap: 作为缓存来优化性能,所有的客户端拉取应用信息读取该 Map
ReadWriteMap: 作为应用实例下线时存储的 Map
两个 Map 之间的关系: 间隔一定时间ReadOnlyMap 去同步 ReadWriteMap
EurekaClient 和 Ribbon 之间也有同步策略,默认 30s
如下图
调整拉取间隔, 关闭 EurekaServer 缓存
当有实例下线, EurekaServer 发起接口调用到EurekaClient, EurekaClient中该接口负责主动拉取服务列表.
调整之后的关系如下图:
代码
EurekaServer
配置
#关闭读取缓存
eureka.server.use-read-only-response-cache=false
主动通知客户端来拉取服务列表
package com.wekj.eureka;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.cloud.netflix.eureka.EurekaDiscoveryClient;
import org.springframework.cloud.netflix.eureka.server.InstanceRegistry;
import org.springframework.cloud.netflix.eureka.server.event.EurekaInstanceCanceledEvent;
import org.springframework.context.annotation.Bean;
import org.springframework.context.event.EventListener;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Component;
import org.springframework.web.client.RestTemplate;
import java.util.List;
/**
* @author 胡说
* @data 2020-08-05 16:27
* 服务的优雅下线:
* 当有实例下线时,主动通知 client 过来拉取最新的服务列表
*/
@Component
public class GraceShutdownProcessor {
private Logger log = LoggerFactory.getLogger(this.getClass());
@Autowired
DiscoveryClient discoveryClient;
@Autowired
RestTemplate restTemplate;
@Autowired
ThreadPoolTaskExecutor cacheExecutor;
private volatile long lastTime= 0;
@Bean
public RestTemplate getRestTemplate() {
return new RestTemplate();
}
@EventListener
public synchronized void onApplicationEvent(EurekaInstanceCanceledEvent event) throws InterruptedException {
// 一次下线会多次触发该事件,做个拦截忽略.
if (System.currentTimeMillis()-lastTime<5000) {
return;
}
log.info("收到应用【{}】实例【{}】下线通知",event.getAppName(),event.getServerId());
notifyDown(event);
log.info("完成【{}】实例【{}】下线通知",event.getAppName(),event.getServerId());
lastTime = System.currentTimeMillis();
}
public void notifyDown(EurekaInstanceCanceledEvent event) {
//获取所有服务名
List<String> services = discoveryClient.getServices();
if (services == null || services.isEmpty()) {
return;
}
for (String serviceName : services) {
//根据服务名获取所有实例
List<ServiceInstance> instances = discoveryClient.getInstances(serviceName);
if (instances == null || instances.isEmpty()) {
continue;
}
//遍历所有实例,逐个通知
for (ServiceInstance instance : instances) {
if (((EurekaDiscoveryClient.EurekaServiceInstance) instance).getInstanceInfo().getInstanceId().equalsIgnoreCase(event.getServerId())) {
continue;
}
cacheExecutor.submit(new SendNotifyRequest(serviceName, instance, restTemplate));
}
}
}
}
EurekaClient
# 优雅上下线:每隔 5s去 eureka-server 拉取应用列表
eureka.client.registry-fetch-interval-seconds=5
# ribbon 主动更新服务列表间隔 单位毫秒
ribbon.ServerListRefreshInterval=5000
通过反射调用,主动拉取服务列表
package com.wekj.boot.eureka;
import cn.hutool.core.util.ReflectUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.lang.reflect.Method;
/**
* @author 胡说
* @data 2020-07-08 16:43
* 处理服务的优雅上下线
*/
@Component
@RestController
public class GraceUpDownProcess {
private Logger log = LoggerFactory.getLogger(this.getClass());
@Autowired
ApplicationContext context;
/**
* 用来主动调用从 eurekaServer 获取服务列表
*/
@GetMapping("invokeRefresh")
public void offline() {
log.debug("开始主动获取服务列表......");
com.netflix.discovery.DiscoveryClient bean = context.getBean(com.netflix.discovery.DiscoveryClient.class);
Method refreshRegistry = ReflectUtil.getMethod(bean.getClass(), "refreshRegistry");
refreshRegistry.setAccessible(true);
ReflectUtil.invoke(bean, "refreshRegistry");
log.debug("主动获取服务列表完成.");
}
}
最后
以上就是俊秀哈密瓜为你收集整理的EurekaClient 优雅上下线方案背景解决思路 前提知识代码的全部内容,希望文章能够帮你解决EurekaClient 优雅上下线方案背景解决思路 前提知识代码所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
发表评论 取消回复