1.路由加载
1. 路由配置
spring:
cloud:
gateway:
routes:
- id : rewritepath
predicates:
- Path=/jzk-app-eureka-dev/jzkApp/**
uri: lb://JZK-EUREKA-DEV
filters:
- RewritePath=/jzk-app-eureka-dev/jzkApp,/jzkAppdev
- id: transform
predicates:
- Path=/mizar-cloud-auth/**
uri: lb://MIZAR-CLOUD-AUTH-DEV
filters:
- RewritePath=/mizar-cloud-auth,/mizar-cloud-auth-dev
- name: RequestRateLimiter
args:
key-resolver: '#{@remoteAddrKeyResolver}'
redis-rate-limiter.replenishRate: 1
redis-rate-limiter.burstCapacity: 1
结合@ConfigurationProperties 的日常使用 , 我们可以知道 routes是一个对象数组,一个route对象包含id ,predicates,url,filters 属性,其中 predicates ,filters 也是对象数组。
直接ctrl + 左键点击 routes ,可以直接 找到
GatewayProperties
@ConfigurationProperties("spring.cloud.gateway")
public class GatewayProperties {
private List<RouteDefinition> routes = new ArrayList();
private List<FilterDefinition> defaultFilters = new ArrayList();
private List<MediaType> streamingMediaTypes;
private boolean failOnRouteDefinitionError;
}
GatewayAutoConfiguration 可以看到 最终的配置变成了List routes
ctrl + 左键 ,可以看到 GatewayAutoConfiguration 持有GatewayProperties 的引用 并且赋值给了PropertiesRouteDefinitionLocator ,RouteDefinitionLocator 路由定义定位器 是不是还有其他的 定位器呢 ?
继续看该类提供的其他Bean
@Bean
@ConditionalOnMissingBean
public PropertiesRouteDefinitionLocator propertiesRouteDefinitionLocator(GatewayProperties properties) {
return new PropertiesRouteDefinitionLocator(properties);
}
//基于内存的路由仓库 默认实现,如果你想, 你可以autowired 这个类, 调用它提供的方法 进行刷新路由, 只要你有合适的触发机会
@Bean
@ConditionalOnMissingBean({RouteDefinitionRepository.class})
public InMemoryRouteDefinitionRepository inMemoryRouteDefinitionRepository() {
return new InMemoryRouteDefinitionRepository();
}
//Composite 混合的,并且带上了 @Primary 注解,我们有理由猜测这个类可能是个组合代理类 :它的作用是 组合多种RouteDefinitionLocator ,提供统一入口
@Bean
@Primary
public RouteDefinitionLocator routeDefinitionLocator(List<RouteDefinitionLocator> routeDefinitionLocators) {
return new CompositeRouteDefinitionLocator(Flux.fromIterable(routeDefinitionLocators));
}
//将RouteDefinition转化为 完整的路由对象Route
@Bean
public RouteLocator routeDefinitionRouteLocator(GatewayProperties properties, List<GatewayFilterFactory> gatewayFilters, List<RoutePredicateFactory> predicates, RouteDefinitionLocator routeDefinitionLocator, ConfigurationService configurationService) {
return new RouteDefinitionRouteLocator(routeDefinitionLocator, predicates, gatewayFilters, properties, configurationService);
}
//又一个 @Primary 这个类是最终 真正Route对象集合访问的真正入口,因为他具备缓存性,如果没有这个类,在响应式系统中 可能会造成 一个请求就重新 获取所有definition-> 转换路由 -> 匹配理由。
@Bean
@Primary
@ConditionalOnMissingBean(
name = {"cachedCompositeRouteLocator"}
)
public RouteLocator cachedCompositeRouteLocator(List<RouteLocator> routeLocators) {
return new CachingRouteLocator(new CompositeRouteLocator(Flux.fromIterable(routeLocators)));
}
//见名知意 它应该和刷新路由相关 ,并且是事件监听触发
@Bean
public RouteRefreshListener routeRefreshListener(ApplicationEventPublisher publisher) {
return new RouteRefreshListener(publisher);
}
RouteLocator
gateway 路由管理 由三层 RouteLocator组成,
分别为 RouteDefinitionRouteLocator 将RouteDefinition转化为 Route 对象,
,CompositeRouteLocator 将路由组合起来,提供统一入口
,CachingRouteLocator 是最终进行路由匹配实际操作的对象, 会监听 RefreshRoutesEvent 事件,重新获取 RouteDefinition集合 来实例化路由,并更新缓存
基于微服务名进行转发
DiscoveryClientRouteDefinitionLocator
上文提到CompositeRouteLocator 会将多个 RouteDefinitionRouteLocator 实例组合起来,提供统一入口,DiscoveryClientRouteDefinitionLocator就是专门根据服务名提供路由匹配规则的
spring.cloud.gateway.discovery.locator.enabled:true 开启服务自动发现,应该说根据服务名自动注册路由
public static class ReactiveDiscoveryClientRouteDefinitionLocatorConfiguration {
@Bean
@ConditionalOnProperty(
name = {"spring.cloud.gateway.discovery.locator.enabled"}
)
public DiscoveryClientRouteDefinitionLocator discoveryClientRouteDefinitionLocator(ReactiveDiscoveryClient discoveryClient, DiscoveryLocatorProperties properties) {
return new DiscoveryClientRouteDefinitionLocator(discoveryClient, properties);
}
}
DiscoveryClientRouteDefinitionLocator
public Flux<RouteDefinition> getRouteDefinitions() {
...
//这里的实例列表是内部封装了 com.netflix.discovery.DiscoveryClient实例, 服务在启动时根据是否拉取服务列表配置,默认为true从注册中心拉取服务列表
return this.serviceInstances.filter((instances) -> {
return !instances.isEmpty();
}).map((instances) -> {
return (ServiceInstance)instances.get(0);
}).filter(includePredicate).map((instance) -> {
//从instance中解析 predicate uri信息
RouteDefinition routeDefinition = this.buildRouteDefinition(urlExpr, instance);
ServiceInstance instanceForEval = new DiscoveryClientRouteDefinitionLocator.DelegatingServiceInstance(instance, this.properties);
...
return routeDefinition;
});
}
那么何时进行路由的刷新呢 ?
RouteRefreshListener
public void onApplicationEvent(ApplicationEvent event) {
if (!(event instanceof ContextRefreshedEvent) && !(event instanceof RefreshScopeRefreshedEvent) && !(event instanceof InstanceRegisteredEvent)) {
if (event instanceof ParentHeartbeatEvent) {
ParentHeartbeatEvent e = (ParentHeartbeatEvent)event;
this.resetIfNeeded(e.getValue());
} else if (event instanceof HeartbeatEvent) {
//如果监听到HeartbeatEvent事件 会进行判断是否 再发布RefreshRoutesEvent事件,此事件将触发路由的更新
HeartbeatEvent e = (HeartbeatEvent)event;
this.resetIfNeeded(e.getValue()); //调用下面reset
}
} else {
this.reset(); //转身又发了个事件 RefreshRoutesEvent
}
}
HeartbeatEvent什么时候触发? ** CloudEurekaClient#onCacheRefreshed**
//com.netflix.discovery.DiscoveryClient 定时任务拉取到新的服务列表 进行缓存更新时,会调用onCacheRefreshed方法,此时进行HeartbeatEvent事故发布,从而被RouteRefreshListener监听到,最终触发路由的更新
protected void onCacheRefreshed() {
super.onCacheRefreshed();
if (this.cacheRefreshedCount != null) {
long newCount = this.cacheRefreshedCount.incrementAndGet();
log.trace("onCacheRefreshed called with count: " + newCount);
this.publisher.publishEvent(new HeartbeatEvent(this, newCount));
}
}
RefreshRoutesEvent 被谁接收了?
CachingRouteLocator
public class CachingRouteLocator implements Ordered, RouteLocator,
ApplicationListener<RefreshRoutesEvent>, ApplicationEventPublisherAware {
private final RouteLocator delegate;
private final Flux<Route> routes;
private Flux<Route> fetch() {
//持有的delegate 是CompositeRouteLocator 它会负责收集包括各种 RouteLocator的实现类 getRoutes方法返回的 Flux<Route>
return this.delegate.getRoutes().sort(AnnotationAwareOrderComparator.INSTANCE);
}
@Override
public Flux<Route> getRoutes() {
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);
}
}
2.基于zookeeper动态路由
1.基于Zookeeper的节点监听机制,每个节点的值都是序列化后的RouteDefinition对象,读取指定路径下的所有子节点,获取所有的路由信息,反序列化获得RouteDefinition对象数组
2.监听所有的子节点的值,进行相应的 RouteDefinition对象 更新/删除
3.监听所有指定路径的子节点列表,找到变化后的子节点列表中不在旧节点列表存在的节点(即新增的路由节点),根据节点获取值,转化成RouteDefinition对象,同时监控此节点值的变化。
4.在初始化 / 触发监听时事件后 ,发布RefreshRoutesEvent事件,让gateway 根据List 创建新的 List 对象,进行路由的替换
核心是 监听RefreshRoutesEvent 事件,从新拉取路由定义进行实例化,路由定义可使用db,nosql,apollo等多种实现,需要刷新的时候发布下事件即可