Cloud Native 6 min read

Refreshing Spring Cloud Gateway Route Cache on Service Instance Changes Using Nacos Events

The article explains why Spring Cloud Gateway may still call a downed service due to a stale CachingRouteLocator cache, and demonstrates how to listen to Nacos InstancesChangeEvent to evict the cache so the gateway always uses the latest service list.

Code Ape Tech Column
Code Ape Tech Column
Code Ape Tech Column
Refreshing Spring Cloud Gateway Route Cache on Service Instance Changes Using Nacos Events

Hello everyone, I'm Chen. Recently, members of my Knowledge Planet were studying the "Spring Cloud Alibaba" column and encountered an issue where a service went offline in a cluster but the gateway still routed requests to it, causing errors.

Root Cause

The gateway uses a CachingRouteLocator which caches routes. When a service is added or removed, the cache is not refreshed promptly, leading to stale routing information.

public class CachingRouteLocator implements Ordered, RouteLocator, ApplicationListener
, ApplicationEventPublisherAware {
    private static final Log log = LogFactory.getLog(CachingRouteLocator.class);
    private static final String CACHE_KEY = "routes";
    private final RouteLocator delegate;
    private final Flux
routes;
    private final Map
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
fetch() {
        return this.delegate.getRoutes().sort(AnnotationAwareOrderComparator.INSTANCE);
    }
    @Override
    public Flux
getRoutes() {
        return this.routes;
    }
    /**
     * Clears the routes cache.
     * @return routes flux
     */
    public Flux
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
    void handleRefresh() { refresh(); }
    @Override
    public int getOrder() { return 0; }
    @Override
    public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
        this.applicationEventPublisher = applicationEventPublisher;
    }
}

Solution: listen to Nacos instance change events and evict the corresponding load‑balancer cache immediately, forcing Spring Cloud Gateway to fetch the latest service list.

Implementation

The following listener subscribes to InstancesChangeEvent and clears the cache for the affected service.

@Component
@Slf4j
public class NacosInstancesChangeEventListener extends Subscriber
{
    @Resource
    private CacheManager defaultLoadBalancerCacheManager;
    @Override
    public void onEvent(InstancesChangeEvent event) {
        log.info("Spring Gateway received instance refresh event: {}, starting cache refresh", JacksonUtils.toJson(event));
        Cache cache = defaultLoadBalancerCacheManager.getCache(SERVICE_INSTANCE_CACHE_NAME);
        if (cache != null) {
            cache.evict(event.getServiceName());
        }
        log.info("Spring Gateway instance refresh completed");
    }
    @Override
    public Class
subscribeType() {
        return InstancesChangeEvent.class;
    }
}

This listener obtains the service name from the event and evicts the corresponding entry in the load‑balancer cache (which implements Spring's Cache interface), ensuring that subsequent requests use the updated service list.

Feel free to test this approach in your own projects.

Final Note (Support the Author)

If you found this article helpful, please like, view, share, or bookmark it; your support motivates me to keep writing. I also run a Knowledge Planet where you can get discounted access to various Spring and microservice tutorials.

load balancingservice discoveryNacosSpring Cloud GatewayCache RefreshCachingRouteLocator
Code Ape Tech Column
Written by

Code Ape Tech Column

Former Ant Group P8 engineer, pure technologist, sharing full‑stack Java, job interview and career advice through a column. Site: java-family.cn

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.