parent
							
								
									008c0f830f
								
							
						
					
					
						commit
						a01a6e88d3
					
				| @ -1,77 +1,33 @@ | ||||
| # 原理分析之如何路由 | ||||
| 
 | ||||
| ## zuul如何路由 | ||||
| Spring Cloud Gateway通过一系列的Filter来进行数据的传输,如下图所示: | ||||
| 
 | ||||
| SOP网关默认使用zuul,当然也默认使用了zuul提供的路由功能。zuul默认使用过滤器来实现路由转发, | ||||
| 我们看下zuul中自带的过滤器: | ||||
|  | ||||
| 
 | ||||
| | 类型 | 顺序 | 过滤器               | 功能                       | | ||||
| | ----- | ---- | ----------------------- | ---------------------------- | | ||||
| | 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默认的路由功能,非常方便。 | ||||
| 当有微服务重新启动时,网关会监听到微服务实例有变更,会重复上述步骤,确保网关存有最新的路由。 | ||||
|  | ||||
| @ -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<ResponseEntity<String>> listGlobalFilters(ServerWebExchange exchange) { | ||||
|         Map<String, GlobalFilter> filterMap = applicationContext.getBeansOfType(GlobalFilter.class); | ||||
|         List<String> filters = filterMap.values() | ||||
|                 .stream() | ||||
|                 .sorted(new Comparator<GlobalFilter>() { | ||||
|                     @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("<br>", filters); | ||||
|         return Mono.just(ResponseEntity.ok(result)); | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
| } | ||||
					Loading…
					
					
				
		Reference in new issue