From a01a6e88d3a59301136f500b309b76cc12755346 Mon Sep 17 00:00:00 2001 From: tanghc Date: Thu, 4 Mar 2021 10:02:59 +0800 Subject: [PATCH] 4.3.0 --- .../90012_原理分析之如何路由.md | 86 +++++-------------- .../controller/FilterPrintController.java | 58 +++++++++++++ 2 files changed, 79 insertions(+), 65 deletions(-) create mode 100644 sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/controller/FilterPrintController.java diff --git a/doc/docs/files/90012_原理分析之如何路由.md b/doc/docs/files/90012_原理分析之如何路由.md index cc14416e..5352cf16 100644 --- a/doc/docs/files/90012_原理分析之如何路由.md +++ b/doc/docs/files/90012_原理分析之如何路由.md @@ -1,77 +1,33 @@ # 原理分析之如何路由 -## zuul如何路由 +Spring Cloud Gateway通过一系列的Filter来进行数据的传输,如下图所示: -SOP网关默认使用zuul,当然也默认使用了zuul提供的路由功能。zuul默认使用过滤器来实现路由转发, -我们看下zuul中自带的过滤器: +![流程图](images/90012_1.png) -| 类型 | 顺序 | 过滤器 | 功能 | -| ----- | ---- | ----------------------- | ---------------------------- | -| pre | -3 | ServletDetectionFilter | 标记处理 Servlet 的类型 | -| pre | -2 | Servlet30WrapperFilter | 包装 HttpServletRequest 请求 | -| pre | -1 | FormBodyWrapperFilter | 包装请求体 | -| pre | 1 | DebugFilter | 标记调试标志 | -| pre | 5 | PreDecorationFilter | 决定路由转发过滤器 | -| route | 10 | RibbonRoutingFilter | serviceId 请求转发 | -| route | 100 | SimpleHostRoutingFilter | url 请求转发 | -| route | 500 | SendForwardFilter | forward 请求转发 | -| post | 0 | SendErrorFilter | 处理有错误的请求响应 | -| post | 1000 | SendResponseFilter | 处理正常的请求响应 | +SOP网关在此基础上新增了几个Filter用来处理自己的逻辑,如:前置校验、结果返回。 -上图就是zuul提供的默认过滤器,可在org.springframework.cloud.netflix.zuul.filters下查看。 +| 过滤器 | 类型 | Order | 功能 | +| ----- | ---- | ----------------------- | ---------------------------- | +|IndexFilter| `自定义` | -2147483648 | 入口过滤器,获取参数、签名校验 | +|ParameterFormatterFilter | 自定义 | -2147482647 | 格式化参数 | +|LimitFilter|`自定义`|-2147482447|限流| +|ForwardPathFilter|系统自带|0 |设置转发的path| +|RouteToRequestUrlFilter|系统自带|10000|设置转发host| +|SopLoadBalancerClientFilter|`自定义`|10100|LoadBalance获取转发实例| +|NettyRoutingFilter|系统自带|2147483647|获取httpclient发送请求| +|ForwardRoutingFilter|系统自带|2147483647|请求分发| +|GatewayModifyResponseGatewayFilter|`自定义`|-2|处理响应结果| -zuul的过滤器顺序值小的优先执行,其中的`PreDecorationFilter`是我们重点关注的类,由它来决定路由转发去向。 +一个完整的请求会自上而下经过这些Filter,下面讲解如何动态设置路由。 -打开PreDecorationFilter类,看到类注释有一句话:`that determines where and how to route based on the supplied` +## 动态设置路由 -翻译过来就是说,决定从哪里获取路由,然后怎样去路由。 +网关启动后会从注册中心拉取微服务实例,然后请求微服务提供的一个接口(`/sop/routes`),获取开放接口信息(被`@Open`注解的接口)。 -PreDecorationFilter类的核心方法是run()方法。找到run方法中这一句代码: +监听处理类在:`com.gitee.sop.bridge.route.NacosRegistryListener` -`Route route = this.routeLocator.getMatchingRoute(requestURI);` +获取到路由信息后,将路由信息缓存到本地,并保存到数据库,代码在:`com.gitee.sop.gatewaycommon.gateway.route.GatewayRouteCache.load` -这句代码很重要,表示路由从哪里获取,如果我们能够重写getMatchingRoute方法那就可以返回自己定义的路由了。 +然后动态设置Gateway路由,代码在:`com.gitee.sop.gatewaycommon.gateway.route.GatewayRouteRepository.refresh` -接下来找到RouteLocator类的定义,发现是通过构造方法传进来的,那么我们就去找使用构造方法的类。(IDEA下右键构造方法--Find Usage) - -在org.springframework.cloud.netflix.zuul.ZuulProxyAutoConfiguration类中找到了定义 - -```java -// pre filters -@Bean -@ConditionalOnMissingBean(PreDecorationFilter.class) -public PreDecorationFilter preDecorationFilter(RouteLocator routeLocator, ProxyRequestHelper proxyRequestHelper) { - return new PreDecorationFilter(routeLocator, this.server.getServlet().getContextPath(), this.zuulProperties, - proxyRequestHelper); -} -``` - -方法默认注入了RouteLocator类,默认注入的实现是CompositeRouteLocator类(通过打断点可以查看)。 - -同时方法上用了`@ConditionalOnMissingBean`注解,表示如果其它地方没有声明,则默认使用这个。 - -因此我们可以自己声明一个PreDecorationFilter,然后注入自定义的RouteLocator就行了。 - -SOP自定义的RouteLocator为:`com.gitee.sop.gatewaycommon.zuul.route.SopRouteLocator`,可自行前往查看。 - -然后再我们的Config中定义: - -```java -/** - * 选取路由 - * @param zuulRouteRepository - * @param proxyRequestHelper - * @return - */ -@Bean -public PreDecorationFilter preDecorationFilter(ZuulRouteRepository zuulRouteRepository, ProxyRequestHelper proxyRequestHelper) { - // 自定义路由 - RouteLocator routeLocator = new SopRouteLocator(zuulRouteRepository); - return new PreDecorationFilter(routeLocator, - this.server.getServlet().getContextPath(), - this.zuulProperties, - proxyRequestHelper); -} -``` - -到此,我们只需要实现RouteLocator接口,就能使用zuul默认的路由功能,非常方便。 +当有微服务重新启动时,网关会监听到微服务实例有变更,会重复上述步骤,确保网关存有最新的路由。 diff --git a/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/controller/FilterPrintController.java b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/controller/FilterPrintController.java new file mode 100644 index 00000000..4ac3969b --- /dev/null +++ b/sop-common/sop-gateway-common/src/main/java/com/gitee/sop/gatewaycommon/gateway/controller/FilterPrintController.java @@ -0,0 +1,58 @@ +package com.gitee.sop.gatewaycommon.gateway.controller; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.cloud.gateway.filter.GlobalFilter; +import org.springframework.context.ApplicationContext; +import org.springframework.core.Ordered; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.server.ServerWebExchange; +import reactor.core.publisher.Mono; + +import java.util.Comparator; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * @author tanghc + */ +@Controller +public class FilterPrintController { + + @Autowired + private ApplicationContext applicationContext; + + @RequestMapping("sop/listGlobalFilters") + public Mono> listGlobalFilters(ServerWebExchange exchange) { + Map filterMap = applicationContext.getBeansOfType(GlobalFilter.class); + List filters = filterMap.values() + .stream() + .sorted(new Comparator() { + @Override + public int compare(GlobalFilter o1, GlobalFilter o2) { + if (o1 instanceof Ordered && o2 instanceof Ordered) { + Ordered order1 = (Ordered) o1; + Ordered order2 = (Ordered) o2; + return Integer.compare(order1.getOrder(), order2.getOrder()); + } + return 0; + } + }) + .map(globalFilter -> { + int order = 0; + if (globalFilter instanceof Ordered) { + Ordered ordered = (Ordered) globalFilter; + order = ordered.getOrder(); + } + return order + ", " + globalFilter.getClass().getSimpleName(); + }) + .collect(Collectors.toList()); + + String result = String.join("
", filters); + return Mono.just(ResponseEntity.ok(result)); + } + + +}