spring的gateway代码详解

                      

由于公司的多个内部项目需要数据交互, 所以手动写了一个路由中转的拦截, 可以将请求转发到不同的项目, 写完以后又看看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 框架中,WebFilterWebHandler 和 OncePerRequestFilter 都是用于处理 Web 请求的组件,但它们在功能和用法上有一些区别。

  1. WebFilter

    • 作用WebFilter 是 Spring Web 模块中定义的用于处理 Web 请求的过滤器接口,可以对请求和响应进行处理,如日志记录、跨域请求处理等。
    • 区别WebFilter 是基于 Servlet 规范的过滤器,与 Spring MVC 或 Spring WebFlux 都可以配合使用,用于实现对请求的统一处理逻辑。
  2. WebHandler

    • 作用WebHandler 是 Spring WebFlux 中定义的用于处理 Web 请求的处理器接口,主要用于处理非阻塞的 Web 请求,并生成响应。
    • 区别WebHandler 是用于处理 WebFlux 中的请求和响应,与 Reactor 或 RxJava 结合使用,支持响应式编程。
  3. OncePerRequestFilter

    • 作用OncePerRequestFilter 是 Spring 框架中定义的过滤器基类,确保每个请求只被过滤一次,避免重复执行过滤逻辑。
    • 区别OncePerRequestFilter 是基于 Servlet 规范的过滤器,通常用于实现一次性的过滤逻辑,如身份验证、日志记录等。

先后顺序

  • 在 Spring Web 应用程序中,OncePerRequestFilter 通常位于 Servlet 过滤器链的最前面,用于确保每个请求只被过滤一次。
  • 接着是 WebFilter,它可以用于对请求进行统一处理,如日志记录、跨域请求处理等。
  • 最后是 WebHandler,用于处理 WebFlux 中的请求和响应,支持响应式编程。

在 Spring 应用程序中,这三者可以配合使用,根据具体的需求和场景来选择合适的组件,配置在合适的顺序中,以实现对 Web 请求的处理和过滤。

springcloud-gateway中使用的就是

FilteringWebHandler implements WebHandler
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值