spring cloud gateway 路由刷新
概述
Spring cloud 配合 nacos 支持定时刷新路由信息,当新增一个微服务时,无需重启网关,就可以将实现请求转发到该服务。
实现
自动更新路由主要是通过 NacosWatch 实现的,NacosWatch 则是通过自动配置完成的:
@Configuration(proxyBeanMethods = false)
@ConditionalOnDiscoveryEnabled
@ConditionalOnBlockingDiscoveryEnabled
@ConditionalOnNacosDiscoveryEnabled
@AutoConfigureBefore({ SimpleDiscoveryClientAutoConfiguration.class,
CommonsClientAutoConfiguration.class })
@AutoConfigureAfter(NacosDiscoveryAutoConfiguration.class)
public class NacosDiscoveryClientConfiguration {
@Bean
@ConditionalOnMissingBean
@ConditionalOnProperty(value = "spring.cloud.nacos.discovery.watch.enabled", matchIfMissing = true)
public NacosWatch nacosWatch(NacosServiceManager nacosServiceManager,
NacosDiscoveryProperties nacosDiscoveryProperties) {
return new NacosWatch(nacosServiceManager, nacosDiscoveryProperties);
}
}
由于 NacosWatch bean 实现了 Lifecycle 接口,容器会调用它的生命周期方法 start()
,该方法内部启动了定时任务:
@Override
public void start() {
if (this.running.compareAndSet(false, true)) {
// ...
// 启动定时任务
this.watchFuture = this.taskScheduler.scheduleWithFixedDelay(
this::nacosServicesWatch, this.properties.getWatchDelay());
}
}
该定时任务,默认每隔 30s 发送一个 HeartbeatEvent:
public void nacosServicesWatch() {
// nacos doesn't support watch now , publish an event every 30 seconds.
this.publisher.publishEvent(
new HeartbeatEvent(this, nacosWatchIndex.getAndIncrement()));
}
这个 HeartbeatEvent 会被 GatewayAutoConfiguration 中的 RouteRefreshListener 接收并处理:
@Bean
@ConditionalOnClass(name = "org.springframework.cloud.client.discovery.event.HeartbeatMonitor")
public RouteRefreshListener routeRefreshListener(ApplicationEventPublisher publisher) {
return new RouteRefreshListener(publisher);
}
RouteRefreshListener 的 onApplicationEvent 方法处理了 HeartbeatEvent:
@Override
public void onApplicationEvent(ApplicationEvent event) {
if (event instanceof ContextRefreshedEvent) {
// ...
}
else if (event instanceof RefreshScopeRefreshedEvent || event instanceof InstanceRegisteredEvent) {
// ...
}
else if (event instanceof ParentHeartbeatEvent) {
// ...
}
else if (event instanceof HeartbeatEvent) {
// 处理了 HeartbeatEvent
HeartbeatEvent e = (HeartbeatEvent) event;
resetIfNeeded(e.getValue());
}
}
private void resetIfNeeded(Object value) {
if (this.monitor.update(value)) {
reset();
}
}
private void reset() {
this.publisher.publishEvent(new RefreshRoutesEvent(this));
}
RouteRefreshListener 处理方式是接收到 HeartbeatEvent 之后,发布了一个新的 RefreshRoutesEvent。所以我们需要看是谁处理了 RefreshRoutesEvent。通过追查发现是 CachingRouteLocator 处理了该事件:
private Flux<Route> fetch() {
return this.delegate.getRoutes().sort(AnnotationAwareOrderComparator.INSTANCE);
}
@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);
}, this::handleRefreshError), this::handleRefreshError);
} catch (Throwable e) {
handleRefreshError(e);
}
}
刷新路由具体来说就是通过 fetch()
方法实现的,该方法会从 nacos 中获取路由信息并更新。因此默认情况下,每隔 30s,网关就会自动更新一次路由信息,如果我们需要改变刷新周期,可以在 bootstrap.yml 中配置如下:
spring:
cloud:
nacos:
discovery:
# 配置从 nacos 服务拉取新服务的时间间隔
watchDelay: 40000
有问题吗?点此反馈!
温馨提示:反馈需要登录