由于公司的多个内部项目需要数据交互, 所以手动写了一个路由中转的拦截, 可以将请求转发到不同的项目, 写完以后又看看gateway的源码, 互相参照
spring-cloud-gateway-core:2.1.2RELEASE
1. 几个关键类
- GatewayAutoConfiguration
自动装配类
-
GatewayProperties
装配spring.cloud.gateway.routes
-
LoadBalancerClientFilter
负载均衡
-
RouteDefinitionRouteLocator
路由的控制器
-
FilteringWebHandler
加载拦截器
2. route加载过程
自动装配类起手
GatewayAutoConfiguration.class:
//0. 注入配置类
@ConfigurationProperties("spring.cloud.gateway")
@Validated
public class GatewayProperties {
private @NotNull @Valid List<RouteDefinition> routes = new ArrayList();
}
//1. 先注入routeDefinitionLocator, 实例为CompositeRouteDefinitionLocator, 是所有的RouteDefinitionLocator聚合
@Bean
@Primary
public RouteDefinitionLocator routeDefinitionLocator(List<RouteDefinitionLocator> routeDefinitionLocators) {
return new CompositeRouteDefinitionLocator(Flux.fromIterable(routeDefinitionLocators));
}
//2.注入RouteLocator(CompositeRouteDefinitionLocator)
@Bean
public RouteLocator routeDefinitionRouteLocator(GatewayProperties properties, List<GatewayFilterFactory> GatewayFilters, List<RoutePredicateFactory> predicates, RouteDefinitionLocator routeDefinitionLocator, @Qualifier("webFluxConversionService") ConversionService conversionService) {
return new RouteDefinitionRouteLocator(routeDefinitionLocator, predicates, GatewayFilters, properties, conversionService);
}
通过这种形式注入, 这时候值是空的
@Bean public GatewayProperties gatewayProperties() { return new GatewayProperties(); }
但是在实例化完成以后, 会调用
ConfigurationPropertiesBindingPostProcessor
从Binder上下文中, 获取到spring.cloud.gateway开头的值, 并填充到GatewayProperties中
JavaBeanBinder.class:
private <T> boolean bind(BeanSupplier<T> beanSupplier,
BeanPropertyBinder propertyBinder, BeanProperty property) {
String propertyName = property.getName();
ResolvableType type = property.getType();
Supplier<Object> value = property.getValue(beanSupplier);
Annotation[] annotations = property.getAnnotations();
Object bound = propertyBinder.bindProperty(propertyName,
Bindable.of(type).withSuppliedValue(value).withAnnotations(annotations));
if (bound == null) {
return false;
}
if (property.isSettable()) {
property.setValue(beanSupplier, bound);
}
else if (value == null || !bound.equals(value.get())) {
throw new IllegalStateException(
"No setter found for property: " + property.getName());
}
return true;
}
GatewayProperties.class:
public void setRoutes(List<RouteDefinition> routes) {
this.routes = routes;
if (routes != null && routes.size() > 0 && this.logger.isDebugEnabled()) {
this.logger.debug("Routes supplied from Gateway Properties: " + routes);
}
}
@Bean
public RouteLocator routeDefinitionRouteLocator(GatewayProperties properties, List<GatewayFilterFactory> GatewayFilters, List<RoutePredicateFactory> predicates, RouteDefinitionLocator routeDefinitionLocator, @Qualifier("webFluxConversionService") ConversionService conversionService)
上面的@Bean中注入了List<GatewayFilterFactory> , 那么spring是如何找到GatewayFilterFactory并加载的, 那是因为@Bean中的入参, 是由spring自动注入的
在@Bean注入时, 会调用构造方法实例化
beanFactory.preInstantiateSingletons();
-> ConstructorResolver.instantiateUsingFactoryMethod
如下图, spring会在容器中找到适合的对象, 并作为agrsToUsed注入
...
Method factoryMethodToUse = null;
ArgumentsHolder argsHolderToUse = null;
Object[] argsToUse = null;
if (explicitArgs != null) {
argsToUse = explicitArgs;
}
else {
Object[] argsToResolve = null;
synchronized (mbd.constructorArgumentLock) {
//从RootBeanDefinition(mbd)中获取构造方法
factoryMethodToUse = (Method) mbd.resolvedConstructorOrFactoryMethod;
if (factoryMethodToUse != null && mbd.constructorArgumentsResolved) {
// Found a cached factory method...
argsToUse = mbd.resolvedConstructorArguments;
if (argsToUse == null) {
argsToResolve = mbd.preparedConstructorArguments;
}
}
}
if (argsToResolve != null) {
//查找并返回构造方法所需要的对象
argsToUse = resolvePreparedArguments(beanName, mbd, bw, factoryMethodToUse, argsToResolve, true);
}
}
...
接下来详细看一下CompositeRouteDefinitionLocator
CompositeRouteDefinitionLocator内容
其规则是由DiscoveryLocatorProperties配置的, 规则是private String urlExpression = "'lb://'+serviceId";
RouteDefinitionRouteLocator.class:
//构造方法
public RouteDefinitionRouteLocator(RouteDefinitionLocator routeDefinitionLocator, List<RoutePredicateFactory> predicates, List<GatewayFilterFactory> gatewayFilterFactories, GatewayProperties gatewayProperties, ConversionService conversionService) {
this.routeDefinitionLocator = routeDefinitionLocator;
this.conversionService = conversionService;
this.initFactories(predicates);
gatewayFilterFactories.forEach((factory) -> {
GatewayFilterFactory var10000 = (GatewayFilterFactory)this.gatewayFilterFactories.put(factory.name(), factory);
});
this.gatewayProperties = gatewayProperties;
}
//解析路由
public Flux<Route> getRoutes() {
return this.routeDefinitionLocator.getRouteDefinitions().map(this::convertToRoute).map((route) -> {
if (this.logger.isDebugEnabled()) {
this.logger.debug("RouteDefinition matched: " + route.getId());
}
return route;
});
}
//解析路由
private Route convertToRoute(RouteDefinition routeDefinition) {
AsyncPredicate<ServerWebExchange> predicate = this.combinePredicates(routeDefinition);
List<GatewayFilter> gatewayFilters = this.getFilters(routeDefinition);
return ((Route.AsyncBuilder)Route.async(routeDefinition).asyncPredicate(predicate).replaceFilters(gatewayFilters)).build();
}
上述代码可以看到, 路由的规则来自于routeDefinitionLocator, 而routeDefinitionLocator的值来自于
CompositeRouteDefinitionLocator.class:
public Flux<RouteDefinition> getRouteDefinitions() {
return this.delegates.flatMap(RouteDefinitionLocator::getRouteDefinitions);
}
CompositeRouteDefinitionLocator中的Flux<RouteDefinitionLocator> delegates包含了三个类,InMemoryRouteDefinitionRepository,PropertiesRouteDefinitionLocator和DiscoveryClientRouteDefinitionLocator
InMemoryRouteDefinitionRepository.class:
public Flux<RouteDefinition> getRouteDefinitions() {
return Flux.fromIterable(this.routes.values());
}
PropertiesRouteDefinitionLocator.class:
public Flux<RouteDefinition> getRouteDefinitions() {
return Flux.fromIterable(this.properties.getRoutes());
}
DiscoveryClientRouteDefinitionLocator.class
public Flux<RouteDefinition> getRouteDefinitions() {
SpelExpressionParser parser = new SpelExpressionParser();
Expression includeExpr = parser.parseExpression(this.properties.getIncludeExpression());
//使用spel表达式提取uri
Expression urlExpr = parser.parseExpression(this.properties.getUrlExpression());
Predicate includePredicate;
if (this.properties.getIncludeExpression() != null && !"true".equalsIgnoreCase(this.properties.getIncludeExpression())) {
includePredicate = (instance) -> {
Boolean include = (Boolean)includeExpr.getValue(this.evalCtxt, instance, Boolean.class);
return include == null ? false : include;
};
} else {
includePredicate = (instance) -> {
return true;
};
}
Flux var10000 = Flux.fromIterable(this.discoveryClient.getServices());
DiscoveryClient var10001 = this.discoveryClient;
var10001.getClass();
return var10000.map(var10001::getInstances).filter((instances) -> {
return !instances.isEmpty();
}).map((instances) -> {
return (ServiceInstance)instances.get(0);
}).filter(includePredicate).map((instance) -> {
String serviceId = instance.getServiceId();
//构造RouteDefinition
RouteDefinition routeDefinition = new RouteDefinition();
routeDefinition.setId(this.routeIdPrefix + serviceId);
String uri = (String)urlExpr.getValue(this.evalCtxt, instance, String.class);
routeDefinition.setUri(URI.create(uri));
ServiceInstance instanceForEval = new DelegatingServiceInstance(instance, this.properties);
Iterator var8 = this.properties.getPredicates().iterator();
Iterator var11;
Map.Entry entry;
String value;
while(var8.hasNext()) {
PredicateDefinition originalx = (PredicateDefinition)var8.next();
PredicateDefinition predicate = new PredicateDefinition();
predicate.setName(originalx.getName());
var11 = originalx.getArgs().entrySet().iterator();
while(var11.hasNext()) {
entry = (Map.Entry)var11.next();
value = this.getValueFromExpr(this.evalCtxt, parser, instanceForEval, entry);
predicate.addArg((String)entry.getKey(), value);
}
routeDefinition.getPredicates().add(predicate);
}
var8 = this.properties.getFilters().iterator();
while(var8.hasNext()) {
FilterDefinition original = (FilterDefinition)var8.next();
//构造FilterDefinition
FilterDefinition filter = new FilterDefinition();
filter.setName(original.getName());
var11 = original.getArgs().entrySet().iterator();
while(var11.hasNext()) {
entry = (Map.Entry)var11.next();
value = this.getValueFromExpr(this.evalCtxt, parser, instanceForEval, entry);
filter.addArg((String)entry.getKey(), value);
}
routeDefinition.getFilters().add(filter);
}
return routeDefinition;
});
}
将RouteDefinition转换为Route, 一共解析了三部分, id, uri和predicates
private Route convertToRoute(RouteDefinition routeDefinition) {
AsyncPredicate<ServerWebExchange> predicate = this.combinePredicates(routeDefinition);
List<GatewayFilter> gatewayFilters = this.getFilters(routeDefinition);
return ((Route.AsyncBuilder)Route.async(routeDefinition).asyncPredicate(predicate).replaceFilters(gatewayFilters)).build();
}
Route的加载过程到处结束, 接下来讲一下一次http请求是如何被"route"的
3.LoadBalance加载过程
public class LoadBalancerClientFilter implements GlobalFilter, Ordered
类继承GlobalFilter, 从自动装配类加载GatewayLoadBalancerClientAutoConfiguration
而所有的GlobalFilter都被放入FilteringWebHandler
@Bean
public FilteringWebHandler filteringWebHandler(List<GlobalFilter> globalFilters) {
return new FilteringWebHandler(globalFilters);
}
FilteringWebHandler.class:
public class FilteringWebHandler implements WebHandler {
protected static final Log logger = LogFactory.getLog(FilteringWebHandler.class);
private final List<GatewayFilter> globalFilters;
public FilteringWebHandler(List<GlobalFilter> globalFilters) {
this.globalFilters = loadFilters(globalFilters);
}
//加载GlobalFilter
private static List<GatewayFilter> loadFilters(List<GlobalFilter> filters) {
return (List)filters.stream().map((filter) -> {
GatewayFilterAdapter gatewayFilter = new GatewayFilterAdapter(filter);
if (filter instanceof Ordered) {
int order = ((Ordered)filter).getOrder();
return new OrderedGatewayFilter(gatewayFilter, order);
} else {
return gatewayFilter;
}
}).collect(Collectors.toList());
}
public Mono<Void> handle(ServerWebExchange exchange) {
//从上下文中获取Route
Route route = (Route)exchange.getRequiredAttribute(ServerWebExchangeUtils.GATEWAY_ROUTE_ATTR);
List<GatewayFilter> gatewayFilters = route.getFilters();
List<GatewayFilter> combined = new ArrayList(this.globalFilters);
//组合所有的Filter并排序
combined.addAll(gatewayFilters);
AnnotationAwareOrderComparator.sort(combined);
if (logger.isDebugEnabled()) {
logger.debug("Sorted gatewayFilterFactories: " + combined);
}
//构造成一个链路调用
return (new DefaultGatewayFilterChain(combined)).filter(exchange);
}
private static class GatewayFilterAdapter implements GatewayFilter {
private final GlobalFilter delegate;
GatewayFilterAdapter(GlobalFilter delegate) {
this.delegate = delegate;
}
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
return this.delegate.filter(exchange, chain);
}
public String toString() {
StringBuilder sb = new StringBuilder("GatewayFilterAdapter{");
sb.append("delegate=").append(this.delegate);
sb.append('}');
return sb.toString();
}
}
LoadBalancerClientFilter.class:
public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
URI url = (URI)exchange.getAttribute(ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR);
String schemePrefix = (String)exchange.getAttribute(ServerWebExchangeUtils.GATEWAY_SCHEME_PREFIX_ATTR);
if (url != null && ("lb".equals(url.getScheme()) || "lb".equals(schemePrefix))) {
ServerWebExchangeUtils.addOriginalRequestUrl(exchange, url);
log.trace("LoadBalancerClientFilter url before: " + url);
ServiceInstance instance = this.choose(exchange);
if (instance == null) {
throw NotFoundException.create(this.properties.isUse404(), "Unable to find instance for " + url.getHost());
} else {
//从web上下文中, 获取uri
URI uri = exchange.getRequest().getURI();
String overrideScheme = instance.isSecure() ? "https" : "http";
if (schemePrefix != null) {
overrideScheme = url.getScheme();
}
//从loadBalance中将服务名转换为具体的ip
URI requestUrl = this.loadBalancer.reconstructURI(new DelegatingServiceInstance(instance, overrideScheme), uri);
log.trace("LoadBalancerClientFilter url chosen: " + requestUrl);
exchange.getAttributes().put(ServerWebExchangeUtils.GATEWAY_REQUEST_URL_ATTR, requestUrl);
return chain.filter(exchange);
}
} else {
return chain.filter(exchange);
}
}
上面的分析可以看到整个拦截的链路, 接下来看一下loadBalance
@Configuration
@ConditionalOnClass({LoadBalancerClient.class, RibbonAutoConfiguration.class, DispatcherHandler.class})
@AutoConfigureAfter({RibbonAutoConfiguration.class})
@EnableConfigurationProperties({LoadBalancerProperties.class})
public class GatewayLoadBalancerClientAutoConfiguration {
public GatewayLoadBalancerClientAutoConfiguration() {
}
@Bean
@ConditionalOnBean({LoadBalancerClient.class})
@ConditionalOnMissingBean({LoadBalancerClientFilter.class})
public LoadBalancerClientFilter loadBalancerClientFilter(LoadBalancerClient client, LoadBalancerProperties properties) {
return new LoadBalancerClientFilter(client, properties);
}
}
看到了熟悉的RibbonAutoConfiguration
@Configuration
@Conditional({RibbonClassesConditions.class})
@RibbonClients
@AutoConfigureAfter(
name = {"org.springframework.cloud.netflix.eureka.EurekaClientAutoConfiguration"}
)
@AutoConfigureBefore({LoadBalancerAutoConfiguration.class, AsyncLoadBalancerAutoConfiguration.class})
@EnableConfigurationProperties({RibbonEagerLoadProperties.class, ServerIntrospectorProperties.class})
public class RibbonAutoConfiguration {
@Bean
public SpringClientFactory springClientFactory() {
SpringClientFactory factory = new SpringClientFactory();
factory.setConfigurations(this.configurations);
return factory;
}
@Bean
@ConditionalOnMissingBean({LoadBalancerClient.class})
public LoadBalancerClient loadBalancerClient() {
//注意这里的SpringClientFactory不是注入的
return new RibbonLoadBalancerClient(this.springClientFactory());
}
}
写在最后:
拦截器有很多, 之前常用的是OncePerRequestFilter
OncePerRequestFilter extends GenericFilterBean -> GenericFilterBean implements Filter
最后说一下WebFilter,WebHandler和OncePerRequestFilter, 这三者的作用, 区别
在 Spring 框架中,WebFilter
、WebHandler
和 OncePerRequestFilter
都是用于处理 Web 请求的组件,但它们在功能和用法上有一些区别。
-
WebFilter:
- 作用:
WebFilter
是 Spring Web 模块中定义的用于处理 Web 请求的过滤器接口,可以对请求和响应进行处理,如日志记录、跨域请求处理等。 - 区别:
WebFilter
是基于 Servlet 规范的过滤器,与 Spring MVC 或 Spring WebFlux 都可以配合使用,用于实现对请求的统一处理逻辑。
- 作用:
-
WebHandler:
- 作用:
WebHandler
是 Spring WebFlux 中定义的用于处理 Web 请求的处理器接口,主要用于处理非阻塞的 Web 请求,并生成响应。 - 区别:
WebHandler
是用于处理 WebFlux 中的请求和响应,与 Reactor 或 RxJava 结合使用,支持响应式编程。
- 作用:
-
OncePerRequestFilter:
- 作用:
OncePerRequestFilter
是 Spring 框架中定义的过滤器基类,确保每个请求只被过滤一次,避免重复执行过滤逻辑。 - 区别:
OncePerRequestFilter
是基于 Servlet 规范的过滤器,通常用于实现一次性的过滤逻辑,如身份验证、日志记录等。
- 作用:
先后顺序:
- 在 Spring Web 应用程序中,
OncePerRequestFilter
通常位于 Servlet 过滤器链的最前面,用于确保每个请求只被过滤一次。 - 接着是
WebFilter
,它可以用于对请求进行统一处理,如日志记录、跨域请求处理等。 - 最后是
WebHandler
,用于处理 WebFlux 中的请求和响应,支持响应式编程。
在 Spring 应用程序中,这三者可以配合使用,根据具体的需求和场景来选择合适的组件,配置在合适的顺序中,以实现对 Web 请求的处理和过滤。
springcloud-gateway中使用的就是
FilteringWebHandler implements WebHandler