Zuul实现请求自定义的过滤转发
背景
最近工作中遇到一个场景,前端请求需要根据一个参数hash到固定的某个服务实例上,由于请求转发部分项目中使用的Zuul是通过Ribbon默认进行转发,在Eureka上选取的实例是不一定的,因此需要考虑如何替代Ribbon或其他方案来实现。
在实现过程中发现方式比较多,将常见的几种记录一下,大体的思路都是通过filter识别匹配到请求,要么绕开Zuul,要么能够告诉Zuul让其用指定的ip进行请求的发送。
实现方式一:定义一个Filter,修改servletRequest属性,绕开ZuulFilter进行自定义的转发
在Debug过程汇总,Filter的执行优先级高于ZuulFilter,因此考虑如果不希望ZuulFilter对请求进行转发处理,是否可以在ZuulFilter之前就对请求进行截获,通过跟踪源码发现,请求到达后会在DispatcherServlet中根据request的特征选择对应的handler进行处理,如下:
// Determine handler for the current request.
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null) {
noHandlerFound(processedRequest, response);
return;
}
选择出对应的mappedHandler后会通过调用执行对应的逻辑,如下:
// Determine handler adapter for the current request.
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
在决定使用哪一个hanlder的逻辑如下:
protected HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {
if (this.handlerMappings != null) {
for (HandlerMapping mapping : this.handlerMappings) {
HandlerExecutionChain handler = mapping.getHandler(request);
if (handler != null) {
return handler;
}
}
}
return null;
}
其中hanlderMappings中包含进程中经所有可用的hanlder,如下:
网关在集成zuul+ribbon后,默认会将请求的URL进行拆分,根据zuul配置的路由规则以及从Ribbon同步的服务实例列表进行比对,看看会否匹配,如果匹配,则就请求就会被ZuulHandlerMapping截获。
为了让请求绕开zuul的截获规则,需要看下源码中getHandler如何返回对应的handler,如下:
// Pattern match?
List<String> matchingPatterns = new ArrayList<>();
for (String registeredPattern : this.handlerMap.keySet()) {
if (getPathMatcher().match(registeredPattern, urlPath)) {
matchingPatterns.add(registeredPattern);
}
else if (useTrailingSlashMatch()) {
if (!registeredPattern.endsWith("/") && getPathMatcher().match(registeredPattern + "/", urlPath)) {
matchingPatterns.add(registeredPattern + "/");
}
}
}
if (!matchingPatterns.isEmpty()) {
matchingPatterns.sort(patternComparator);
if (logger.isTraceEnabled() && matchingPatterns.size() > 1) {
logger