Spring Cloud Zuul过滤器介绍及使用

过滤器类型

Zuul 中的过滤器跟我们之前使用的 javax.servlet.Filter 不一样,javax.servlet.Filter 只有一种类型,可以通过配置 urlPatterns 来拦截对应的请求。

而 Zuul 中的过滤器总共有 4 种类型,且每种类型都有对应的使用场景。

1)pre

可以在请求被路由之前调用。适用于身份认证的场景,认证通过后再继续执行下面的流程。

2)route

在路由请求时被调用。适用于灰度发布场景,在将要路由的时候可以做一些自定义的逻辑。

3)post

在 route 和 error 过滤器之后被调用。这种过滤器将请求路由到达具体的服务之后执行。适用于需要添加响应头,记录响应日志等应用场景。

4)error

处理请求时发生错误时被调用。在执行过程中发送错误时会进入 error 过滤器,可以用来统一记录错误信息。

请求生命周期

可以通过图 1 看出整个过滤器的执行生命周期, 

Spring Cloud Zuul过滤器介绍及使用_第1张图片
图 1  过滤器生命周期


通过上面的图可以清楚地知道整个执行的顺序,请求发过来首先到 pre 过滤器,再到 routing 过滤器,最后到 post 过滤器,任何一个过滤器有异常都会进入 error 过滤器。

通过 com.netflix.zuul.http.ZuulServlet 也可以看出完整执行顺序,ZuulServlet 类似 Spring-Mvc 的 DispatcherServlet,所有的 Request 都要经过 ZuulServlet 的处理。

ZuulServlet 源码如下所示:

 
  1. @Override
  2. public void service(javax.servlet.ServletRequest servletRequest, javax.servlet.ServletResponse servletResponse)
  3. throws ServletException, IOException {
  4. try {
  5. init((HttpServletRequest) servletRequest, (HttpServletResponse) servletResponse);
  6. RequestContext context = RequestContext.getCurrentContext();
  7. context.setZuulEngineRan();
  8. try {
  9. preRoute();
  10. } catch (ZuulException e) {
  11. error(e);
  12. postRoute();
  13. return;
  14. }
  15. try {
  16. route();
  17. } catch (ZuulException e) {
  18. error(e);
  19. postRoute();
  20. return;
  21. }
  22. try {
  23. postRoute();
  24. } catch (ZuulException e) {
  25. error(e);
  26. return;
  27. }
  28. } catch (Throwable e) {
  29. error(new ZuulException(e, 500, "UNHANDLED_EXCEPTION_" + e.getClass().getName()));
  30. } finally {
  31. RequestContext.getCurrentContext().unset();
  32. }
  33. }

使用过滤器

我们创建一个 pre 过滤器,来实现 IP 黑名单的过滤操作,代码如下所示。

 
  1. public class IpFilter extends ZuulFilter {
  2.  
  3. // IP黑名单列表
  4. private List blackIpList = Arrays.asList("127.0.0.1");
  5.  
  6. public IpFilter() {
  7. super();
  8. }
  9.  
  10. @Override
  11. public boolean shouldFilter() {
  12. return true
  13. }
  14.  
  15. @Override
  16. public String filterType() {
  17. return "pre";
  18. }
  19.  
  20. @Override
  21. public int filterOrder() {
  22. return 1;
  23. }
  24.  
  25. @Override
  26. public Object run() {
  27. RequestContext ctx = RequestContext.getCurrentContext();
  28. String ip = IpUtils.getIpAddr(ctx.getRequest());
  29. // 在黑名单中禁用
  30. if (StringUtils.isNotBlank(ip) && blackIpList.contains(ip)) {
  31.  
  32. ctx.setSendZuulResponse(false);
  33. ResponseData data = ResponseData.fail("非法请求 ", ResponseCode.NO_AUTH_CODE.getCode());
  34. ctx.setResponseBody(JsonUtils.toJson(data));
  35. ctx.getResponse().setContentType("application/json; charset=utf-8");
  36. return null;
  37. }
  38. return null;
  39. }
  40. }

由代码可知,自定义过滤器需要继承 ZuulFilter,并且需要实现下面几个方法:

1)shouldFilter

是否执行该过滤器,true 为执行,false 为不执行,这个也可以利用配置中心来实现,达到动态的开启和关闭过滤器。

2)filterType

过滤器类型,可选值有 pre、route、post、error。

3)filterOrder

过滤器的执行顺序,数值越小,优先级越高。

4)run

执行自己的业务逻辑,本段代码中是通过判断请求的 IP 是否在黑名单中,决定是否进行拦截。blackIpList 字段是 IP 的黑名单,判断条件成立之后,通过设置 ctx.setSendZuulResponse(false),告诉 Zuul 不需要将当前请求转发到后端的服务了。通过 setResponseBody 返回数据给客户端。

你可能感兴趣的:(Docker)