过滤器类型与请求生命周期
PRE:这种过滤器在请求被路由之前调用。可利用这种过滤器实现身份验证、在集群中选择请求的微服务、记录调试信息等。ROUTING:这种过滤器将请求路由到微服务。这种过滤器用于构建发送给微服务的请求,并使用Apache HttpClient或Netfilx Ribbon请求微服务。POST:这种过滤器在路由到微服务后执行。这种过滤器可用来为响应添加标准的HTTP Header、收集统计信息和指标、将响应从微服务发送给客户端等。ERROR:在其他阶段发生错误时执行该过滤器。
除了默认的过滤器类型,Zuul还允许创建自定义的过滤器类型。例如,可以定制一种STATIC类型的过滤器,直接在Zuul中生成响应,而不将请求转发到后端的微服务。
内置过滤器详解
@EnableZuulProxy是最常用的注解,是@EnableZuulServer的增强版。
RequestContext扩展了ConcurrentHashMap,用于在过滤器之间传递消息。它的数据保存在每个请求的ThreadLocal中。用于存储请求路由到哪里、错误、HttpServlet、HttpServletResponse等信息。
@EnableZuulServer所启用的过滤器
- pre 类型过滤器
ServletDetectionFilter:该过滤器用于检查请求是否通过Spring Dispatcher。检查后,通过FilterConstants.IS_DISPATCHER_SERVLET_REQUEST_KEY(isDispatcherServletRequest)设置布尔值。FormBodyWrapperFilter:解析表单数据,并为请求重新编码。DebugFilter:顾名思义,调试用的过滤器,当设置zuul.include-debug-header=true或zuul.debug.request=true,并在请求时,加上debug=true的参数,例如$ZUUL_HOST:ZUUL_PORT/path?debug=true,就会开启该过滤器。该过滤器就会把RequestContext.setDebugRouting()、RequestContext.setDebugRequest()设为 true。
- route 类型过滤器
SendForwardFilter:该过滤器使用Servlet RequestDispatcher转发请求,转发位置存储在RequestContext的属性FilterConstants.FOREWARD_TO_KEY 中。这对转发到Zuul自身的端点很有用。可以将路由改成:
zuul:
routes:
abc:
path: /path-a/**
url: forward:/path-b
然后访问$ZUUL_HOST:ZUUL_PORT/path-a ,观察该过滤器的执行过程。
- post 类型过滤器
SendResponseFilter:将Zuul所代理请求的的响应写入当前响应。
- error 类型过滤器
SendErrorFilter:如果RequestContext.getThrowable() 不为 null,那么默认就会转发到/error,也可以设置error.path属性修改默认的转发路径。
@EnableZuulProxy所启用的过滤器
如果使用注解@EnableZuulProxy,那么除上述过滤器之外,Spring Cloud还会安装以下过滤器:
- pre 类型过滤器
PreDecorationFilter:该过滤器根据提供的RouteLocator确定路由到的地址,以及怎样去路由。该路由器也可为后端请求设置各种代理相关的 header。
- route 类型过滤器
RibbonRoutingFilter:该过滤器使用Ribbon,Hystrix和可插拔的 HTTP 客户端发送请求。serviceId 在RequestContext的属性FilterConstants.SERVICE_ID_KEY(RequestContext.getCurrentContext().get("serviceId")) 中。该过滤器可使用不同的 HTTP 客户端。
Apache HttpClient:默认的 HTTP 客户端。Squareup OkHttpClient v3:如需使用该客户端,需保证com.squareup.okhttp3的依赖在 classpath 中,并设置ribbon.okhttp.enabled = true。Netflix Ribbon HTTP client:设置ribbon.restclient.enabled = true即可启用该 HTTP 客户端。需要注意的是,该客户端有一定限制,例如不支持 PATCH 方法,另外,它有内置的重试机制。
SimpleHostRoutingFilter:该过滤器通过Apache HttpClient向指定的 URL 发送请求。URL 在RequestContext.getRouteHost()中。
编写Zuul过滤器
- 编写自定义
Zuul过滤器:
public class PreRequestLogFilter extends ZuulFilter {
private static final Logger LOGGER = LoggerFactory.getLogger(PreRequestLogFilter.class);
@Override
public String filterType() {
return FilterConstants.PRE_TYPE;
}
@Override
public int filterOrder() {
return FilterConstants.PRE_DECORATION_FILTER_ORDER - 1;
}
@Override
public boolean shouldFilter() {
return true;
}
@Override
public Object run() {
RequestContext context = RequestContext.getCurrentContext();
HttpServletRequest request = context.getRequest();
PreRequestLogFilter.LOGGER.info((String.format("send %s request to %s", request.getMethod(), request.getRequestURI())));
return null;
}
}
由代码可知,自定义的 Zuul Filter 需实现以下几个方法:
filterType:返回过滤器的类型。有pre、route、post、error等几种取值,分别对应上文的几种过滤器。详细可以参考com.netflix.zuul.ZuulFilter.filterType()中的注释。filterOrder:返回一个 int 值来指定过滤器的执行顺序,不同的过滤器允许返回相同的数字。shouldFilter:返回一个 boolean 值来判断该过滤器是否要执行,true 表示执行,false 表示不执行。run:过滤器的具体逻辑。本例中,我们让它打印了请求的 HTTP 方法以及请求的地址。
- 在启动类上添加:
@Bean
public PreRequestLogFilter preRequestLogFilter() {
return new PreRequestLogFilter();
}
- 启动用户微服务,
访问http://127.0.0.1:8040/microservice-provider-user/1,获得如下日志:
INFO 9107 --- [nio-8040-exec-3] c.i.z.m.PreRequestLogFilter : send GET request to /microservice-provider-user/1
说明自定义Zuul过滤器被执行了。
禁用Zuul过滤器
Spring Cloud默认为Zuul编写并启用了一些过滤器,例如DebugFilter、FormBodyWrapperFilter、PreDecorationFilter等。这些过滤器都存放在spring-cloud-netflix-core这个 Jar 包的org.springframework.cloud.netflix.zuul.filters包中。
一些场景下,我们想要禁用掉部分过滤器,只需设置zuul.SimpleClassName所对应的过滤器。以过滤器org.springframework.cloud.netflix.zuul.filters.post.SendResponseFilter为例,只需设置zuul.SendResponseFilter.post.disable=true ,即可禁用该过滤器。
