Zuul的过滤器
过滤器是Zuul的核心组件,本博客主要详细讨论一下Zuul的过滤器。
主要包括如下内容:
过滤器类型与请求生命周期
内置过滤器详解
2.1@EnableZuulServer所启用的过滤器
2.2@EnableZuulProxy所启用的过滤器
2.3编写Zuul过滤器
过滤器类型与请求生命周期
Zuul大部分功能都是过滤器实现的。Zuul定义了4种标准的过滤器,分别对应不同的请求的生命周期。
PRE:这种过滤器在请求被路由之前调用。可利用这种过滤器实现身份验证、在集群中选择请求的微服务,记录调试信息等。
ROUTING:这种过滤器将请求路由到微服务。这种过滤器用于构建发送给微服务的请求,并使用Apache HttpClient或Netflix Ribbon请求微服务。
POST:这种过滤器在路由到微服务以后执行。这种过滤器可用来为响应添加标准的HTTP header、收集统计信息和指标、将响应从微服务发送给客户端等。
ERROR:在其他阶段发送错误时执行该过滤器。
除了默认的过滤器类型,Zuul还允许创建自定义的过滤器类型。例如,可以定制一种STATIC类型的过滤器,直接在Zuul中生成响应,而不将请求转发到后端的微服务。
Zuul请求的生命周期如下图,该图详细描述了各种类型的过滤器的执行顺序。
Spring Cloud默认为Zuul编写并启用了一些过滤器,这些过滤器有什么作用呢?我们不妨按照@EnableZuulServer、@EnableZuulProxy两个注解进行展开,相信大家对这两个注解都不陌生(至少都见过吧)。如果觉得陌生也没有关系,可将@EnableZuulProxy简单理解为@EnableZuulServer的增强版。事实上,当Zuul与Eureka、Ribbon等组件配合使用时,@EnableZuulProxy是我们常用的注解。
在Spring Cloud的官方文档中,只说@EnableZuulServer是一个“空白”的Zuul,那么究竟空白在什么地方呢?与@EnableZuulProxy到底有什么区别呢?诸多问题,都将在本文找到答案。
在此之前,我们先理解什么是RequestContext:
RequestContext:用于在过滤器之间传递消息。它的数据保存在每个请求的ThreadLocal中。它用于存储请求路由到哪里、错误、HttpServletRequest、HttpServletResponse都存储在RequestContext中。RequestContext扩展了ConcurrentHashMap,所以,任何数据都可以存储在上下文(RequestContext)中。
2.1@EnableZuulServer所启用的过滤器
1.pre类型过滤器
(1) ServletDetectionFilter:该过滤器用于检查请求是否通过Spring Dispatcher。检查后,通过FilterConstants.IS_DISPATCHER_SERVLET_REQUEST_KEY设置布尔值。
(2) FormBodyWrapperFilter:解析表单数据,并为请求重新编码。
(3) DebugFilter:顾名思义,调试用的过滤器。可设置zuul.include-debug-header=true或者设置zuul.debug.request=true,并在请求时,加上debug=true的参数,例如$ZUUL_HOST:ZUUL_PORT/path?debug=true开启该过滤器。这样,该过滤器就会把RequestContext.setDebugRouting()以及RequestContext.setDebugRequest()设为true。
2.route类型过滤器
SendForwardFilter:该过滤器使用Servlet RequestDispatcher转发请求,转发位置存储在RequestContext的属性FilterConstants.FORWARD_TO_KEY中。可以将路由设置成:
zuul:
routes:
abc:
path: /abc/**
url: forward:/abc
然后访问$ZUUL_HOST:ZUUL_PORT/abc/**,观察该过滤器的执行过程。
3.post类型过滤器
SendResponseFilter:将Zuul所代理的微服务的响应写入当前响应。
4.error类型过滤器
SendErrorFilter:如果RequestContext.getThrowable()不为null,那么默认就会转发到/error,也可以设置error.path属性修改默认的转发路径。
2.2@EnableZuulProxy所启用的过滤器
如果使用注解@EnableZuulProxy,那么除上述过滤器之外,Spring Cloud还会安装以下过滤器:
1.pre类型过滤器
PreDecorationFilter:该过滤器根据提供的RouteLocator确定路由到的地址,以及怎样去路由。该路由器也可为后端请求设置各种代理相关的header。
2.2.route类型过滤器
(1) RibbonRoutingFilter:该过滤器使用Ribbon,Hystrix和可插拔的HTTP客户端发送请求。serviceId在FilterConstants.SERVICE_TO_KEY中。该过滤器可使用不同的HTTP客户端,例如
Apache HttpClient:默认的HTTP客户端
SquareupOkHttpClient v3:如需使用该客户端,需保证com.squareup.okhttp3的依赖在classpath中,并设置ribbon.okhttp.enabled = true。
Netflix Ribbon HTTP client:设置ribbon.restclient.enabled = true即可启用该HTTP客户端。需要注意的是,该客户端有一定限制,例如不支持PATCH方法,另外,它有内置的重试机制。
(2) SimpleHostRoutingFilter:该过滤器通过Apache HttpClient向指定的URL发送请求。URL在RequestContext.getRouteHost()中。
*建议大家看一下源码,特别是RibbonRoutingFilter过滤器源码。
*形如如下内容路由不经过RibbonRoutingFilter,而是走SimpleHostRoutingFilter过滤器。
zuul:
routes:
user-route:
url:http://127.0.0.1:8080/
path:/user/**
*大家可以通过/filters端点查看过滤器详情
*目前FormBodyWrapperFilter的代码实现并不是很高效,若你没有表单提交,可禁用,从而获得更好的性能表现。
2.3编写Zuul过滤器
编写过滤器很简单,只需继承抽象类ZuulFilter即可。
2.3.1创建项目
复制cloud-register-gateway-zuul微服务修改为cloud-register-gateway-zuul-filter微服务
2.3.2编写自定义Zuul过滤器
public class PreRequestLogFilter extends ZuulFilter {
private static final Logger logger = LoggerFactory.getLogger(PreRequestLogFilter.class);
@Override
public String filterType() {
//"pre"类型过滤器
return FilterConstants.PRE_TYPE;
}
@Override
public int filterOrder() {
//在PreDecorationFilter之前执行
return FilterConstants.PRE_DECORATION_FILTER_ORDER - 1;
}
@Override
public boolean shouldFilter() {
return true;
}
@Override
public Object run() {
RequestContext ctx = RequestContext.getCurrentContext();
HttpServletRequest request = ctx.getRequest();
logger.info("send {} requtest to {}", request.getMethod(), request.getRequestURI().toString());
return null;
}
zuul实现filter的方法:
filterType:返回过滤器的类型:pre、route、post、error,分别对应上文的几种过滤器。详情可参考com.netflix.zuul.ZuulFilter.filterType()中的注释。
filterOrder:返回int值,来确定filter的执行顺序,不同的过滤器允许返回相同的数字。
shouldFilter:返回boolean确定filter是否执行。
run:过滤器执行内容。
2.3.3修改启动类,为启动类添加以下内容。
@Bean
public PreRequestLogFilter preRequestLogFilter()
{
return new PreRequestLogFilter();
}
2.3.4测试
启动cloud-discovery-eureka,port=8001
启动cloud-register-user,port=8002
启动loud-register-file-upload-filter,port=8025
2.3.4结果
出现如下日志,说明ok了
本博客只是简单的demo,过滤器还可以做安全认证、灰度发布、限流等功能。
http://www.fmi.com.cn/index.php?m=content&c=index&a=show&catid=9&id=618616
http://cloud.51cto.com/art/201704/536854.htm
http://www.itmuch.com/spring-cloud-sum/spring-cloud-ratelimit/
1.gitee: https://gitee.com/StarskyBoy/cloud
2.github: https://github.com/StarskyBoy/cloud