zuul路由配置路径前缀问题
背景
我们项目组刚使用spring cloud项目不久,项目运行了几个月也没有使用zuul作网关,之前是使用nginx配置不同路径规则进行不能服务端口的转发。nginx配置如下:
location ^~ /user/ {
proxy_pass http://xx.xx.xx.xx:xxxx;
}
location ^~ /product/ {
proxy_pass http://xx.xx.xx.xx:xxxx;
}
location ^~ /cert/ {
proxy_pass http://xx.xx.xx.xx:xxxx;
}
...
路径转发后对原始路径没有做任何修改,因此服务中Rest接口定义路径的时候会保留 /user/、/product/、/cert/这样的前缀。如:
@RestController
@RequestMapping("/user/") // 保留服务模块定位前缀
public class XXXController {
// code
}
为什么要使用zuul?
zuul作为网关除了做路由转发之外,它提供的过滤器可以做权限控制、限流、API监控等功能,因此引入zuul对项目的长期维护来说是有好处的。
出现问题
添加了一个gateway-zuul工程,在application.yml中添加了如下路由配置:
spring:
application:
name: gateway-zuul
server:
port: 8888
zuul:
routes:
api-user:
path: /user/**
service-id: xxx-USER-SERVICE
启动工程, 访问接口:http://localhost:8888/user/xxx/xxx, 返回404。
排查问题
-
首先调试代码排查,发现路由过程是按照下面类的流程走的
ZuulServletFilter -> ZuulRunner -> route() FilterProcessor -> runFilters('route') ZuulFilter -> run() RibbonRoutingFilter
发现RibbonRoutingFilter执行中RequestContext中的RequestURI的值已经是/xxx/xxx了
前缀/user不见了
-
猜测在preRouting 阶段应该是做了path截断了.
发现pre截断有一个Filter类叫 PreDecorationFilter,
它的run方法是这样的:
@Override public Object run() { RequestContext ctx = RequestContext.getCurrentContext(); final String requestURI = this.urlPathHelper.getPathWithinApplication(ctx.getRequest()); Route route = this.routeLocator.getMatchingRoute(requestURI); if (route != null) { // .... } // ... }
在解析requestURI获取组装对象Route的时候发现 route中的path前缀被截取了,继续深入
发现了SimpleRouteLocator这个类中获取路由对象的方法:protected Route getRoute(ZuulRoute route, String path) { if (route == null) { return null; } if (log.isDebugEnabled()) { log.debug("route matched=" + route); } String targetPath = path; String prefix = this.properties.getPrefix(); if (path.startsWith(prefix) && this.properties.isStripPrefix()) { targetPath = path.substring(prefix.length()); } if (route.isStripPrefix()) { int index = route.getPath().indexOf("*") - 1; if (index > 0) { String routePrefix = route.getPath().substring(0, index); targetPath = targetPath.replaceFirst(routePrefix, ""); prefix = prefix + routePrefix; } } Boolean retryable = this.properties.getRetryable(); if (route.getRetryable() != null) { retryable = route.getRetryable(); } return new Route(route.getId(), targetPath, route.getLocation(), prefix, retryable, route.isCustomSensitiveHeaders() ? route.getSensitiveHeaders() : null); }
中截取route前缀的地方是
if (route.isStripPrefix()) { //这里有个判断 int index = route.getPath().indexOf("*") - 1; if (index > 0) { String routePrefix = route.getPath().substring(0, index); targetPath = targetPath.replaceFirst(routePrefix, ""); prefix = prefix + routePrefix; } }
发现截取条件表达式route.isStripPrefix()的值是在ZuulProperties类中获取,而ZuulProperties类的数据来源就是application.yml中的zuul配置。
问题定位。
解决问题
设置ZuulProperties类中的stripPrefix属性,在application.yml相应route配置的地方加上配置属性strip-prefix:fase,如下:
zuul:
routes:
api-user:
path: /user/**
service-id: CASH-USER-CENTER-SERVICE
strip-prefix: false
重启服务,发现路径不再被截断,问题解决!
网友评论