mirror of
https://gitee.com/durcframework/SOP.git
synced 2025-08-11 12:56:28 +08:00
4.3.0
This commit is contained in:
@@ -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));
|
||||
}
|
||||
|
||||
|
||||
}
|
Reference in New Issue
Block a user