Spring Cloud Gateway + Nacos 实现服务上下线无缝切换

本文涉及的产品
网络型负载均衡 NLB,每月750个小时 15LCU
传统型负载均衡 CLB,每月750个小时 15LCU
应用型负载均衡 ALB,每月750个小时 15LCU
简介: Spring Cloud Gateway + Nacos 实现服务上下线无缝切换

大家好,我是不才陈某~

最近知识星球的球友在学习星球中的《精尽Spring Cloud Alibaba》专栏提到一个问题,相信也有很多人在线上环境遇到过,或许也因此被批过:一个集群中有某个服务突然下线,但是网关还是会去请求这个实例,所以线上就报错了,报错信息如下图:

究其原因到底为何呢?有没有一种靠谱的解决方案呢?别着急,往下看

产生原因

Gateway中有个缓存 CachingRouteLocator ,而网关服务使用的是lb模式,服务在上线或者下线之后,未能及时刷新这个缓存,相应的源码如下:

public class CachingRouteLocator implements Ordered, RouteLocator,
  ApplicationListener<RefreshRoutesEvent>, ApplicationEventPublisherAware {
 private static final Log log = LogFactory.getLog(CachingRouteLocator.class);
 private static final String CACHE_KEY = "routes";
 private final RouteLocator delegate;
 private final Flux<Route> routes;
 private final Map<String, List> cache = new ConcurrentHashMap<>();
 private ApplicationEventPublisher applicationEventPublisher;
 public CachingRouteLocator(RouteLocator delegate) {
  this.delegate = delegate;
  routes = CacheFlux.lookup(cache, CACHE_KEY, Route.class)
    .onCacheMissResume(this::fetch);
 }
 private Flux<Route> fetch() {
  return this.delegate.getRoutes().sort(AnnotationAwareOrderComparator.INSTANCE);
 }
 @Override
 public Flux<Route> getRoutes() {
  return this.routes;
 }
 /**
  * Clears the routes cache.
  * @return routes flux
  */
 public Flux<Route> refresh() {
  this.cache.clear();
  return this.routes;
 }
 @Override
 public void onApplicationEvent(RefreshRoutesEvent event) {
  try {
   fetch().collect(Collectors.toList()).subscribe(list -> Flux.fromIterable(list)
     .materialize().collect(Collectors.toList()).subscribe(signals -> {
      applicationEventPublisher
        .publishEvent(new RefreshRoutesResultEvent(this));
      cache.put(CACHE_KEY, signals);
     }, throwable -> handleRefreshError(throwable)));
  }
  catch (Throwable e) {
   handleRefreshError(e);
  }
 }
 private void handleRefreshError(Throwable throwable) {
  if (log.isErrorEnabled()) {
   log.error("Refresh routes error !!!", throwable);
  }
  applicationEventPublisher
    .publishEvent(new RefreshRoutesResultEvent(this, throwable));
 }
 @Deprecated
 /* for testing */ void handleRefresh() {
  refresh();
 }
 @Override
 public int getOrder() {
  return 0;
 }
 @Override
 public void setApplicationEventPublisher(
   ApplicationEventPublisher applicationEventPublisher) {
  this.applicationEventPublisher = applicationEventPublisher;
 }
}

那么解决方案就自然能够想出来,只需要在服务下线时能够去实时的刷新这个缓存自然就解决了

解决方案

这里通过去监听 Nacos 实例刷新事件,一旦出现实例发生变化马上删除缓存。在删除负载均衡缓存后,Spring Cloud Gateway 在处理请求时发现没有缓存会重新拉取一遍服务列表,这样之后都是用的是最新的服务列表了,也就达到了我们动态感知上下线的目的。

代码如下:

@Component
@Slf4j
public  class NacosInstancesChangeEventListener extends Subscriber<InstancesChangeEvent> {
    @Resource
    private CacheManager defaultLoadBalancerCacheManager;
    @Override
    public void onEvent(InstancesChangeEvent event) {
        log.info("Spring Gateway 接收实例刷新事件:{}, 开始刷新缓存", JacksonUtils.toJson(event));
        Cache cache = defaultLoadBalancerCacheManager.getCache(SERVICE_INSTANCE_CACHE_NAME);
        if (cache != null) {
            cache.evict(event.getServiceName());
        }
        log.info("Spring Gateway 实例刷新完成");
    }
    @Override
    public Class<? extends com.alibaba.nacos.common.notify.Event> subscribeType() {
        return InstancesChangeEvent.class;
    }
}

这里通过继承的方式监听 Nacos 的 InstancesChangeEvent,在 onEvent 接收到实例刷新的信息后直接删除对应服务的负载均衡缓存,缓存的名字是定义在 Spring Gateway 的相关代码中的,直接引入即可,Cache 则是继承自 Spring Cache 接口,负载均衡缓存也继承了 Cache 接口,有了 Cache 接口就可以直接使用其接口定义的 evict 方法即可,而缓存的 key 名就则就是服务名,在 InstancesChangeEvent 中,通过 getServiceName 就可以得到服务名。

这里就不演示了,有兴趣的小伙伴可以测试一下

相关实践学习
SLB负载均衡实践
本场景通过使用阿里云负载均衡 SLB 以及对负载均衡 SLB 后端服务器 ECS 的权重进行修改,快速解决服务器响应速度慢的问题
负载均衡入门与产品使用指南
负载均衡(Server Load Balancer)是对多台云服务器进行流量分发的负载均衡服务,可以通过流量分发扩展应用系统对外的服务能力,通过消除单点故障提升应用系统的可用性。 本课程主要介绍负载均衡的相关技术以及阿里云负载均衡产品的使用方法。
相关文章
|
2月前
|
存储 数据可视化 Java
基于MicrometerTracing门面和Zipkin实现集成springcloud2023的服务追踪
Sleuth将会停止维护,Sleuth最新版本也只支持springboot2。作为替代可以使用MicrometerTracing在微服务中作为服务追踪的工具。
133 1
|
11天前
|
JavaScript Java Kotlin
深入 Spring Cloud Gateway 过滤器
Spring Cloud Gateway 是新一代微服务网关框架,支持多种过滤器实现。本文详解了 `GlobalFilter`、`GatewayFilter` 和 `AbstractGatewayFilterFactory` 三种过滤器的实现方式及其应用场景,帮助开发者高效利用这些工具进行网关开发。
|
16天前
|
消息中间件 监控 Java
如何将Spring Boot + RabbitMQ应用程序部署到Pivotal Cloud Foundry (PCF)
如何将Spring Boot + RabbitMQ应用程序部署到Pivotal Cloud Foundry (PCF)
31 6
|
16天前
|
Java 关系型数据库 MySQL
如何将Spring Boot + MySQL应用程序部署到Pivotal Cloud Foundry (PCF)
如何将Spring Boot + MySQL应用程序部署到Pivotal Cloud Foundry (PCF)
36 5
|
16天前
|
缓存 监控 Java
如何将Spring Boot应用程序部署到Pivotal Cloud Foundry (PCF)
如何将Spring Boot应用程序部署到Pivotal Cloud Foundry (PCF)
27 5
|
1月前
|
负载均衡 Java 应用服务中间件
Gateway服务网关
Gateway服务网关
52 1
Gateway服务网关
|
5天前
|
API
Istio 使用ingress和gateway两种方式公开服务
本文档指导您完成Istio网关的部署与配置。首先安装`istiod`(步骤略过)。接着,创建`ingress.yaml`文件,定义Istio入口网关的服务、部署及权限设置,通过`kubectl apply -f ingress.yaml`命令应用。最后,创建Ingress资源,指定主机名、后端服务及TLS配置,实现对外部请求的路由管理。
13 0
|
1月前
|
JSON Java 测试技术
SpringCloud2023实战之接口服务测试工具SpringBootTest
SpringBootTest同时集成了JUnit Jupiter、AssertJ、Hamcrest测试辅助库,使得更容易编写但愿测试代码。
58 3
|
19天前
|
负载均衡 Java API
项目中用的网关Gateway及SpringCloud
Spring Cloud Gateway 是一个功能强大、灵活易用的API网关解决方案。通过配置路由、过滤器、熔断器和限流等功能,可以有效地管理和保护微服务。本文详细介绍了Spring Cloud Gateway的基本概念、配置方法和实际应用,希望能帮助开发者更好地理解和使用这一工具。通过合理使用Spring Cloud Gateway,可以显著提升微服务架构的健壮性和可维护性。
25 0
|
3月前
|
Java 开发者 Spring
Spring Cloud Gateway 中,过滤器的分类有哪些?
Spring Cloud Gateway 中,过滤器的分类有哪些?
74 3