gateway 服务发现路由
前言
设置 spring.cloud.gateway.discovery.locator.enabled=true, 这样就可用启动自动发现服务路由,不用单独配路由
spring:
cloud:
nacos:
discovery:
username: nacos
password: nacos
server-addr: 127.0.0.1:8848
namespace: dev
gateway:
discovery:
locator:
lowerCaseServiceId: true
enabled: true
- 默认实现 GatewayDiscoveryClientAutoConfiguration.java
public static List<PredicateDefinition> initPredicates() {
ArrayList<PredicateDefinition> definitions = new ArrayList();
PredicateDefinition predicate = new PredicateDefinition();
predicate.setName(NameUtils.normalizeRoutePredicateName(PathRoutePredicateFactory.class));
// 默认匹配服务id为前缀的路由
predicate.addArg("pattern", "'/'+serviceId+'/**'");
definitions.add(predicate);
return definitions;
}
public static List<FilterDefinition> initFilters() {
ArrayList<FilterDefinition> definitions = new ArrayList();
FilterDefinition filter = new FilterDefinition();
filter.setName(NameUtils.normalizeFilterFactoryName(RewritePathGatewayFilterFactory.class));
// 默认匹配后会去掉服务id部分路径
String regex = "'/' + serviceId + '/?(?<remaining>.*)'";
String replacement = "'/${remaining}'";
filter.addArg("regexp", regex);
filter.addArg("replacement", replacement);
definitions.add(filter);
return definitions;
}
1、默认匹配服务id为前缀的路由
2、默认匹配后会去掉服务id部分路径
3、示例: /sys/version 会匹配sys服务,匹配后的转发路径就是/version
请求流程
1 首先 DispatcherHandler.java 的handle方法执行
@Override
public Mono<Void> handle(ServerWebExchange exchange) {
if (this.handlerMappings == null) {
return createNotFoundError();
}
if (CorsUtils.isPreFlightRequest(exchange.getRequest())) {
return handlePreFlight(exchange);
}
return Flux.fromIterable(this.handlerMappings)
// getHandler方法执行
.concatMap(mapping -> mapping.getHandler(exchange))
.next()
.switchIfEmpty(createNotFoundError())
.flatMap(handler -> invokeHandler(exchange, handler))
.flatMap(result -> handleResult(exchange, result));
}
2 AbstractHandlerMapping mapping.getHandler(exchange)这个方法执行
@Override
public Mono<Object> getHandler(ServerWebExchange exchange) {
// 执行实现类getHandlerInternal
return getHandlerInternal(exchange).map(handler -> {
if (logger.isDebugEnabled()) {
logger.debug(exchange.getLogPrefix() + "Mapped to " + handler);
}
ServerHttpRequest request = exchange.getRequest();
if (hasCorsConfigurationSource(handler) || CorsUtils.isPreFlightRequest(request)) {
CorsConfiguration config = (this.corsConfigurationSource != null ?
this.corsConfigurationSource.getCorsConfiguration(exchange) : null);
CorsConfiguration handlerConfig = getCorsConfiguration(handler, exchange);
config = (config != null ? config.combine(handlerConfig) : handlerConfig);
if (config != null) {
config.validateAllowCredentials();
}
if (!this.corsProcessor.process(config, exchange) || CorsUtils.isPreFlightRequest(request)) {
return NO_OP_HANDLER;
}
}
return handler;
});
}
3 RoutePredicateHandlerMapping getHandlerInternal(exchange)执行
// 路由定义
private final RouteLocator routeLocator;
protected Mono<?> getHandlerInternal(ServerWebExchange exchange) {
if (this.managementPortType == RoutePredicateHandlerMapping.ManagementPortType.DIFFERENT && this.managementPort != null && exchange.getRequest().getURI().getPort() == this.managementPort) {
return Mono.empty();
} else {
exchange.getAttributes().put(ServerWebExchangeUtils.GATEWAY_HANDLER_MAPPER_ATTR, this.getSimpleName());
// 获取路由
return this.lookupRoute(exchange).flatMap((r) -> {
exchange.getAttributes().remove(ServerWebExchangeUtils.GATEWAY_PREDICATE_ROUTE_ATTR);
if (this.logger.isDebugEnabled()) {
this.logger.debug("Mapping [" + this.getExchangeDesc(exchange) + "] to " + r);
}
exchange.getAttributes().put(ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR, r);
return Mono.just(this.webHandler);
}).switchIfEmpty(Mono.empty().then(Mono.fromRunnable(() -> {
exchange.getAttributes().remove(ServerWebExchangeUtils.GATEWAY_PREDICATE_ROUTE_ATTR);
if (this.logger.isTraceEnabled()) {
this.logger.trace("No RouteDefinition found for [" + this.getExchangeDesc(exchange) + "]");
}
})));
}
}
// 通过这个方法获取路由
protected Mono<Route> lookupRoute(ServerWebExchange exchange) {
// 获取路由routeLocator.getRoutes()
return this.routeLocator.getRoutes().concatMap((route) -> {
return Mono.just(route).filterWhen((r) -> {
exchange.getAttributes().put(ServerWebExchangeUtils.GATEWAY_PREDICATE_ROUTE_ATTR, r.getId());
return (Publisher)r.getPredicate().apply(exchange);
}).doOnError((e) -> {
this.logger.error("Error applying predicate for route: " + route.getId(), e);
}).onErrorResume((e) -> {
return Mono.empty();
});
}).next().map((route) -> {
if (this.logger.isDebugEnabled()) {
this.logger.debug("Route matched: " + route.getId());
}
this.validateRoute(route, exchange);
return route;
});
}
4 CachingRouteLocator routeLocator.getRoutes()执行
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;
// 这里会进行缓存处理,没有缓存时通过fetch执行代理获取
this.routes = CacheFlux.lookup(this.cache, "routes", Route.class).onCacheMissResume(this::fetch);
}
private Flux<Route> fetch() {
return this.delegate.getRoutes().sort(AnnotationAwareOrderComparator.INSTANCE);
}
public Flux<Route> getRoutes() {
return this.routes;
}
public Flux<Route> refresh() {
this.cache.clear();
return this.routes;
}
// 关键就是RefreshRoutesEvent事件会刷新路由缓存
public void onApplicationEvent(RefreshRoutesEvent event) {
try {
this.fetch().collect(Collectors.toList()).subscribe((list) -> {
Flux.fromIterable(list).materialize().collect(Collectors.toList()).subscribe((signals) -> {
this.applicationEventPublisher.publishEvent(new RefreshRoutesResultEvent(this));
this.cache.put("routes", signals);
}, this::handleRefreshError);
}, this::handleRefreshError);
} catch (Throwable var3) {
this.handleRefreshError(var3);
}
}
private void handleRefreshError(Throwable throwable) {
if (log.isErrorEnabled()) {
log.error("Refresh routes error !!!", throwable);
}
this.applicationEventPublisher.publishEvent(new RefreshRoutesResultEvent(this, throwable));
}
public int getOrder() {
return 0;
}
public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
this.applicationEventPublisher = applicationEventPublisher;
}
}
默认启用了路由缓存,通过监听RefreshRoutesEvent事件使缓存刷新
5 RefreshRoutesEvent事件是通过这个监听器RouteRefreshListener.reset()发出的
public class RouteRefreshListener implements ApplicationListener<ApplicationEvent> {
private final ApplicationEventPublisher publisher;
private HeartbeatMonitor monitor = new HeartbeatMonitor();
public RouteRefreshListener(ApplicationEventPublisher publisher) {
Assert.notNull(publisher, "publisher may not be null");
this.publisher = publisher;
}
public void onApplicationEvent(ApplicationEvent event) {
if (event instanceof ContextRefreshedEvent) {
ContextRefreshedEvent refreshedEvent = (ContextRefreshedEvent)event;
if (!WebServerApplicationContext.hasServerNamespace(refreshedEvent.getApplicationContext(), "management")) {
this.reset();
}
} else if (!(event instanceof RefreshScopeRefreshedEvent) && !(event instanceof InstanceRegisteredEvent)) {
if (event instanceof ParentHeartbeatEvent) {
ParentHeartbeatEvent e = (ParentHeartbeatEvent)event;
this.resetIfNeeded(e.getValue());
} else if (event instanceof HeartbeatEvent) {
HeartbeatEvent e = (HeartbeatEvent)event;
// 1 这里可能执行发布
this.resetIfNeeded(e.getValue());
}
} else {
// 2 这里执行发布
this.reset();
}
}
private void resetIfNeeded(Object value) {
if (this.monitor.update(value)) {
this.reset();
}
}
// 这个地方发出
private void reset() {
this.publisher.publishEvent(new RefreshRoutesEvent(this));
}
可用看到有两种地方会执行reset方法,
1、HeartbeatEvent事件 2、RefreshScopeRefreshedEvent、InstanceRegisteredEvent
6 集成nacos后通过NacosWatch发送HeartbeatEvent事件
public class NacosWatch implements ApplicationEventPublisherAware, SmartLifecycle, DisposableBean {
private static final Logger log = LoggerFactory.getLogger(NacosWatch.class);
private Map<String, EventListener> listenerMap = new ConcurrentHashMap<>(16);
private final AtomicBoolean running = new AtomicBoolean(false);
private final AtomicLong nacosWatchIndex = new AtomicLong(0);
private ApplicationEventPublisher publisher;
private ScheduledFuture<?> watchFuture;
private NacosServiceManager nacosServiceManager;
private final NacosDiscoveryProperties properties;
private final ThreadPoolTaskScheduler taskScheduler;
public NacosWatch(NacosServiceManager nacosServiceManager,
NacosDiscoveryProperties properties) {
this.nacosServiceManager = nacosServiceManager;
this.properties = properties;
this.taskScheduler = getTaskScheduler();
}
@Deprecated
public NacosWatch(NacosServiceManager nacosServiceManager,
NacosDiscoveryProperties properties,
ObjectProvider<ThreadPoolTaskScheduler> taskScheduler) {
this.nacosServiceManager = nacosServiceManager;
this.properties = properties;
this.taskScheduler = taskScheduler.stream().findAny()
.orElseGet(NacosWatch::getTaskScheduler);
}
private static ThreadPoolTaskScheduler getTaskScheduler() {
ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler();
taskScheduler.setBeanName("Nacos-Watch-Task-Scheduler");
taskScheduler.initialize();
return taskScheduler;
}
@Override
public void setApplicationEventPublisher(ApplicationEventPublisher publisher) {
this.publisher = publisher;
}
@Override
public void start() {
if (this.running.compareAndSet(false, true)) {
EventListener eventListener = listenerMap.computeIfAbsent(buildKey(),
event -> new EventListener() {
@Override
public void onEvent(Event event) {
if (event instanceof NamingEvent) {
List<Instance> instances = ((NamingEvent) event)
.getInstances();
Optional<Instance> instanceOptional = selectCurrentInstance(
instances);
instanceOptional.ifPresent(currentInstance -> {
resetIfNeeded(currentInstance);
});
}
}
});
NamingService namingService = nacosServiceManager
.getNamingService(properties.getNacosProperties());
try {
namingService.subscribe(properties.getService(), properties.getGroup(),
Arrays.asList(properties.getClusterName()), eventListener);
}
catch (Exception e) {
log.error("namingService subscribe failed, properties:{}", properties, e);
}
// 1 这里启用定时任务,频率properties.getWatchDelay()
this.watchFuture = this.taskScheduler.scheduleWithFixedDelay(
this::nacosServicesWatch, this.properties.getWatchDelay());
}
}
public void nacosServicesWatch() {
// 2 这里启用定时任务,执行的方法,默认30s
// nacos doesn't support watch now , publish an event every 30 seconds.
this.publisher.publishEvent(
new HeartbeatEvent(this, nacosWatchIndex.getAndIncrement()));
}
}
默认watch事件间隔,spring.cloud.nacos.discovery.watchDelay=30000ms,所以新实例上线后会有0-30s的随机时延
处理
监听InstancesChangeEvent事件,发送RefreshRoutesEvent事件
@Slf4j
@RequiredArgsConstructor
@Component
public class GatewayStartListener implements ApplicationListener<ApplicationReadyEvent> {
private final ApplicationEventPublisher publisher;
@Override
public void onApplicationEvent(ApplicationReadyEvent event) {
log.info("started:{}", event.getTimeTaken());
NotifyCenter.registerSubscriber(new Subscriber<InstancesChangeEvent>() {
@Override
public void onEvent(InstancesChangeEvent event) {
log.info("onEvent:{}", event);
publisher.publishEvent(new RefreshRoutesEvent(event));
}
@Override
public Class<? extends Event> subscribeType() {
return InstancesChangeEvent.class;
}
});
log.info("started registerSubscriber InstancesChangeEvent");
}
}