我是靠谱客的博主 俊秀哈密瓜,最近开发中收集的这篇文章主要介绍EurekaClient 优雅上下线方案背景解决思路 前提知识代码,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

背景

服务的提供者下线时,调用者依然会发送请求到下线的服务提供者实例上,导致请求失败影响用户体验

解决思路

服务提供者下线时,及时通知调用者,从负载均衡列表中删除下线的实例

 

前提知识

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 优雅上下线方案背景解决思路 前提知识代码所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(63)

评论列表共有 0 条评论

立即
投稿
返回
顶部