• Spring Cloud Alibaba实现服务的无损下线功能


    1、背景

    最近用到了Spring Cloud Alibaba开发微服务,在开发的过程中发现,当我们的服务上线下线的时候,我们的Spring Cloud Gateway需要一段时间才能感知到,那么有没有办法能够让服务立即感知到呢?答案是可以的

    此种实现方式是我自己记录下,目前未在生产环境中使用,此处记录是为了以后遇到此种场景,可以找到一种解决方案

    2、解决方案

    2.1 找到通过负载均衡组件获取可用服务信息的地方

    获取到一个可用的服务实例
    通过一顿Debug之后,从上图中可知,我们获取到可用的服务实例信息,会从缓存中获取,那么如果当服务下线后,我们清空这个服务的缓存的信息,那么下次在获取这个服务的信息就是最新的了,那么问题就解决了。

    2.2 解决思路

    服务下线时序图

    1. 服务提供方或消费方,同时监听 Spring Cloud Config中的某个配置文件,比如lossless文件
    2. 服务下线时, 第一步:nacos上将自身这个实例下线。 第二步: 通过api更新lossless 文件中的内容,记录服务下线的服务名加上时间戳
    3. 消费者或网关,监听到lossless配置文件的变更,获取到服务名,然后将这个服务名对应的缓存清空

    3、部分实现代码

    3.1 引入jar

    <dependencies>
    <dependency>
    <groupId>com.alibaba.cloudgroupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discoveryartifactId>
    dependency>
    <dependency>
    <groupId>com.alibaba.cloudgroupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-configartifactId>
    dependency>
    <dependency>
    <groupId>com.alibaba.nacosgroupId>
    <artifactId>nacos-spring-contextartifactId>
    <version>1.1.1version>
    dependency>
    <dependency>
    <groupId>org.projectlombokgroupId>
    <artifactId>lombokartifactId>
    dependency>
    <dependency>
    <groupId>org.springframework.cloudgroupId>
    <artifactId>spring-cloud-starter-loadbalancerartifactId>
    dependency>
    <dependency>
    <groupId>org.springframework.cloudgroupId>
    <artifactId>spring-cloud-starter-bootstrapartifactId>
    dependency>
    <dependency>
    <groupId>org.springframework.bootgroupId>
    <artifactId>spring-boot-starter-webartifactId>
    dependency>
    dependencies>

    3.2 编写服务下线方法

    @Component
    @Slf4j
    public class LosslessOfflineApi {
    @Resource
    private NacosConfigManager nacosConfigManager;
    @Resource
    private NacosServiceManager nacosServiceManager;
    @Resource
    private NacosDiscoveryProperties nacosDiscoveryProperties;
    /**
    * 服务下线
    *
    * @throws NacosException NacosException
    */
    public void offlineService() throws NacosException {
    log.info("触发服务下线 serviceName:[{}]", nacosDiscoveryProperties.getService());
    nacosServiceManager.nacosServiceShutDown();
    nacosConfigManager.getConfigService()
    .publishConfig(NacosConstant.DATA_ID, NacosConstant.GROUP,
    nacosDiscoveryProperties.getService() + NacosConstant.SPLIT + System.currentTimeMillis());
    }
    }

    此处需要注意的是: 使用nacosConfigManager.getConfigService().publishConfig 发布配置,此处更新配置中的 服务名+时间戳,如果只是更新服务名,则可能不会触发监听事件。

    3.3 监听配置变更,清除服务缓存

    3.3.1 使@NacosConfigListener注解生效

    1、引入配置

    <dependency>
    <groupId>com.alibaba.nacosgroupId>
    <artifactId>nacos-spring-contextartifactId>
    <version>1.1.1version>
    dependency>

    2、开启注解

    @Configuration
    @EnableNacos(globalProperties =
    @NacosProperties(serverAddr = "${spring.cloud.nacos.config.server-addr}")
    )
    public class NacosConfiguration {
    }

    3、注解生效参考文档

    https://github.com/nacos-group/nacos-spring-project
    https://github.com/alibaba/spring-cloud-alibaba/issues/458

    3.3.2 监听配置、清除缓存

    @Component
    @RequiredArgsConstructor
    @Slf4j
    public class ListenerConfigChange {
    @Resource
    private DefaultLoadBalancerCacheManager defaultLoadBalancerCacheManager;
    @Resource
    private NacosServiceManager nacosServiceManager;
    @Resource
    private NacosDiscoveryProperties nacosDiscoveryProperties;
    @Resource
    private NacosServiceDiscovery nacosServiceDiscovery;
    @Resource
    private NacosConfigManager nacosConfigManager;
    @NacosConfigListener(groupId = GROUP, dataId = DATA_ID)
    public void configChange(String config) {
    log.warn("==>接收到 无损服务下线 配置变更:[{}]", config);
    String serviceName = config.split(SPLIT)[0];
    log.info("需要无损下线的服务名:[{}]", serviceName);
    Cache cache = defaultLoadBalancerCacheManager.getCache(SERVICE_INSTANCE_CACHE_NAME);
    if (null != cache) {
    cache.evict(serviceName);
    log.info("失效serviceName:[{}]的缓存", serviceName);
    }
    }
    }

    4、实现

    4.1 服务准备

    服务 端口 提供api 备注
    nacos-lossless-gateway 9001 /consumer/** 路由到consumer服务
    nacos-feign-consumer 9002 /fetchProviderServerInfo 通过feign接口,获取provider服务的ip和port
    nacos-provider-9003 9003 /shutdown 从nacos server上下线服务,发布配置变更
    nacos-provider-9004 9004 /shutdown 从nacos server上下线服务,发布配置变更

    解释:
    1、通过gateway访问 http://localhost:9001/consumer/fetchProviderServerInfo将会返回provideripport信息。
    2、http://localhost:9003/shutdown 将会将自己从nacos server下线,并且操作 nacos config,变更配置文件的内容。
    3、gatewayconsumer监听到配置变更,更新服务的缓存,从而下次访问,不会访问到这个下线的服务。

    4.2 演示

    演示

    5、完整代码

    https://gitee.com/huan1993/spring-cloud-alibaba-parent/tree/master/nacos-lossless-offline

    6、参考链接

    1、https://github.com/nacos-group/nacos-spring-project
    2、https://github.com/alibaba/spring-cloud-alibaba/issues/458

  • 相关阅读:
    肝了一周的八万字Redis实战篇
    仪器仪表制造业采购数字化方案:集中采购系统为供采双方打造更高效运转平台
    【Angular1】基础使用及各类方法
    LuatOS-SOC接口文档(air780E)--disp - disp库已合并到u8g2库,API等价
    三丰云免费服务器
    你的业务代码中Spring声明式事务处理正确了吗?
    单链表的排序问题
    mysql误删误操作恢复数据,比传统方式和binlog2sql更快速用的恢复方式-reverse_sql恢复数据(单表多表)
    脉冲编码器A、B、Z相正余弦波转换为RS-485输出
    通过一道笔试题目,进行缓存与内存的性能比较及其分析测试
  • 原文地址:https://www.cnblogs.com/huan1993/p/17204685.html