Spring Cloud Gateway中自定义ReactiveDiscoveryClient仅调用一次,如何实现定期刷新?
首先,你的日志只打印一次的核心原因是Spring Cloud LoadBalancer的缓存机制——你在构建ServiceInstanceListSupplier时使用了.withCaching(),这会把从ReactiveDiscoveryClient获取到的实例列表缓存起来,默认场景下缓存不会主动过期,所以Flux.defer()里的逻辑只会执行一次。
下面给你几个可行的解决思路,兼顾定期刷新和健康检查功能:
1. 调整Loadbalancer缓存过期时间(最简单的方案)
直接通过配置让缓存定期过期,这样Loadbalancer会自动重新调用你的getInstances()方法获取最新实例。在application.yml中添加以下配置:
spring: cloud: loadbalancer: cache: ttl: 30s # 每30秒刷新一次实例缓存 capacity: 100 # 缓存容量,按需设置
这个方案不需要修改代码,只需要调整配置,就能让你的DemoReactiveDiscoveryClient定期执行实例获取逻辑。
2. 自定义ServiceInstanceListSupplier并集成健康检查
你提到自己实现ServiceInstanceListSupplier时缺少健康检查功能,其实可以基于内置的DiscoveryClientServiceInstanceListSupplier来扩展,既保留从自定义ReactiveDiscoveryClient获取实例的逻辑,又能复用官方的健康检查能力。修改你的AppConfig:
@Configuration public class AppConfig { @Bean ReactiveDiscoveryClient reactiveDiscoveryClient() { return new DemoReactiveDiscoveryClient(); } @Bean public ServiceInstanceListSupplier discoveryClientServiceInstanceListSupplier( ConfigurableApplicationContext context, ReactiveDiscoveryClient discoveryClient) { return ServiceInstanceListSupplier.builder() // 基于自定义的ReactiveDiscoveryClient构建基础实例提供者 .withBase(new DiscoveryClientServiceInstanceListSupplier(discoveryClient, context.getEnvironment())) .withHealthChecks() // 启用内置健康检查 .withCaching(30, TimeUnit.SECONDS) // 设置缓存刷新间隔 .build(context); } }
这样既实现了实例的定期刷新,又自动集成了Spring Cloud LoadBalancer的健康检查功能,不需要自己重复造轮子。
3. 主动触发缓存刷新(适合需要精准控制刷新时机的场景)
如果需要在特定事件(比如配置变更、外部通知)触发时刷新实例列表,可以通过LoadBalancerCacheManager主动清空缓存:
@Component public class InstanceRefreshTrigger { @Autowired private LoadBalancerCacheManager cacheManager; // 示例:定时任务每30秒刷新一次 @Scheduled(fixedRate = 30000) public void refreshInstanceCache() { // 清空serviceInstances缓存,触发重新获取实例 cacheManager.getCache("serviceInstances").clear(); } }
这个方案适合需要灵活控制刷新时机的场景,比如你可以监听自定义事件,当外部服务发布新实例时主动触发刷新。
另外,注意检查你的Gateway路由配置,uri: lb://service中的service要和你的服务ID(say-hello)保持一致,否则可能无法正确找到实例。
内容的提问来源于stack exchange,提问作者Liam

