Spring Cloud(七):服务网关zuul过滤器

2023-11-07

上文介绍了Zuul的基本使用与路由功能,本文接着介绍Zuul的核心概念 —— Zuul过滤器(filter)。

Zuul的功能基本通过Zuul过滤器来实现(类比于Struts的拦截器,只是Struts拦截器用到责任链模式,Zuul则是通过FilterProcessor来控制执行),在不同的阶段,通过不同类型的过滤器来实现相应的功能。

Zuul过滤器

过滤器类型

zuul的过滤器根据对HTTP请求的不同处理阶段包括如下四种类型

  • pre :在请求转发到后端目标服务之前执行,一般用于请求认证、确定路由地址、日志记录等
  • route :转发请求,使用Apache HttpClient 或 Ribbon来构造对目标服务的请求
  • post :在目标服务返回结果后对结果进行处理,比如添加响应头、收集统计性能数据等
  • error :在请求处理的整个流程中如果出现错误,则会触发error过滤器执行,对错误进行处理

客户端请求经过zuul过滤器处理的流程如下图

zuul-filter

zuul使用RequestContext来在过滤器之间传递数据,数据存于每个request的ThreadLocal,包含请求路由到哪里,错误,HttpServletRequest,HttpServletResponse 等这些数据都存储于RequestContext中。RequestContext 扩展了ConcurrentHashMap,所以我们可以根据需要将信息存于context中进行传递。

@EnableZuulProxy vs @EnableZuulServer

zuul提供了两个注解 @EnableZuulProxy, @EnableZuulServer,来启用不同的过滤器集合。@EnableZuulProxy 启用的过滤器 是@EnableZuulServer 的超集, 它包含了@EnableZuulServer 的所有过滤器,proxy主要多了一些提供路由功能的过滤器(可见@EnableZuulServer 不提供路由功能,作为server模式而不是代理模式运行)

@EnableZuulServer 注解启用的过滤器包括

filter类型 实现类 filter顺序值 功能说明
pre ServletDetectionFilter -3 检测请求是否通过Spring Dispatcher,并在RequestContext 中添加一个key为isDispatcherServletRequest, 值为true(不通过则为false)的属性
pre FormBodyWrapperFilter
-1 解析Form data,为请求的下游进行重新编码
pre DebugFilter 1 如果请求参数设置了debug,则会将RequestContext.setDebugRouting() ,RequestContext.setDebugRequest() 设置为ture
route SendForwardFilter 500 使用RequestDispatch servlet来转发请求,转发地址存于RequestContext中key为FilterConstants.FORWARDTOKEY的属性中,对于转发到当前应用的接口比较有用
post
SendResponseFilter 1000 将代理请求的响应内容写到当前的响应中
error SendErrorFilter 0 如果RequestContext.getThrowable() 不为空,则会转发到/error,可以通过error.path来改变默认的转发路径/error

@EnableZuulProxy 除了上面的过滤器,还包含如下过滤器

filter类型 实现类 filter顺序值 功能说明
pre PreDecorationFilter 5 确定路由到哪里,如何路由,依赖提供的RouteLocator,同时也为下游请求设置多个与proxy相关的header
route RibbonRoutingFilter 10 使用ribbon,hystrix,以及内嵌的http client来发送请求,可在RequestContext中通过FilterConstants.SERVICEIDKEY 来找到路由Service的ID
route SimpleHostRoutingFilter 100 使用Apache httpClient来发送请求到一个预先确定的url,可通过RequestContext.getRouteHost()来获取urls

由上可见@EnableZuulServer 注解并不包含往后端服务负载均衡地路由请求的代理功能,@EnableZuulProxy的PreDecorationFilter,RibbonRoutingFilter过滤器才能担当此任。PreDecorationFilter通过提供的DiscoveryClientRouteLocator 从 DiscoveryClient(如Eureka)与属性文件中加载路由定义, 为每个serviceId创建一个route,新服务添加进来,路由也会动态刷新。路由确定了,在RibbonRoutingFilter 中通过ribbon与hystrix结合来向后端目标服务发起请求,并进行负载均衡。过滤器的顺序值表示在同类型过滤器中的执行顺序,值越小越先执行。

自定义Zuul过滤器

自定义的zuul过滤器与框架自带过滤器类似,包括四部分

  1. 过滤器类型,包括pre, route, post
  2. 过滤器顺序,定义在同类型过滤器中的执行顺序,数值越小越先执行
  3. 是否执行过滤,通过一些条件判断来确定是否执行该过滤器
  4. 过滤器执行体,定义具体执行的操作

比如我们需要在Http请求头中设置一个值,供请求链路的下游环节访问,则可以自定义一个过滤器如下,

@Component
public class ReqIdPreFilter extends ZuulFilter {

    @Override
    public String filterType() {
        return FilterConstants.PRE_TYPE;
    }

    @Override
    public int filterOrder() {
        return FilterConstants.PRE_DECORATION_FILTER_ORDER - 1; //在PreDecorationFilter过滤器之前执行
    }

    @Override
    public boolean shouldFilter() {
        return true;
    }

    @Override
    public Object run() throws ZuulException {
        RequestContext ctx = RequestContext.getCurrentContext();
        ctx.addZuulRequestHeader("reqId", UUID.randomUUID().toString());
        return null;
    }
}

在请求的后续环节,比如后端服务的filter或接口中,则可直接从HttpServletRequest 获取该header值,如

@GetMapping("hello/reqId")
public String getReqId(HttpServletRequest request) {
    return "hello-service返回:" + request.getHeader("reqId");
}

Zuul的错误处理

在zuul过滤器的生命周期中,如果任何一个环节抛出异常,则error过滤器会被执行,SendErrorFilter只有当RequestContext.getThrowable()不为null时才会运行,会设置javax.servlet.error.* 属性到request中,然后将请求转发到spring boot的error page, 默认为BasicErrorController实现的/error接口。 有时候我们需要将返回响应格式进行统一,而默认的/error接口实现可能不满足要求,则可以自定义/error接口。需要实现ErrorController 接口以使默认的BasicErrorController 失效。

@RestController
public class ZuulErrorController implements ErrorController {

    @RequestMapping("/error")
    public Map<String, String> error(HttpServletRequest request){
        Map<String, String> result = Maps.newHashMap();
        result.put("code", request.getAttribute("javax.servlet.error.status_code").toString());
        result.put("message", request.getAttribute("javax.servlet.error.message").toString());
        result.put("exception", request.getAttribute("javax.servlet.error.exception").toString());
        return result;
    }

    @Override
    public String getErrorPath() {
        return "/error";
    }
}

Zuul的服务降级

当调用服务出现超时或异常时,在zuul侧可提供回调进行服务降级,返回默认响应结果,如

@Component
public class MyFallbackProvider implements FallbackProvider {

    @Override
    public String getRoute() {
        return null; //指定这个回调针对的route Id,如果对所有route,则返回* 或null
    }

    @Override
    public ClientHttpResponse fallbackResponse(String route, Throwable cause) {
        if (cause instanceof HystrixTimeoutException) {
            return response(HttpStatus.GATEWAY_TIMEOUT);
        } else {
            return response(HttpStatus.INTERNAL_SERVER_ERROR);
        }
    }

    private ClientHttpResponse response(final HttpStatus status) {
        return new ClientHttpResponse() {
            @Override
            public HttpStatus getStatusCode() throws IOException {
                return status;
            }
            @Override
            public int getRawStatusCode() throws IOException {
                return status.value();
            }
            @Override
            public String getStatusText() throws IOException {
                return status.getReasonPhrase();
            }
            @Override
            public void close() {
            }
            @Override
            public InputStream getBody() throws IOException {
                Map<String, String> result = Maps.newLinkedHashMap();
                result.put("code", "" + status.value());
                String msg = HttpStatus.GATEWAY_TIMEOUT == getStatusCode() ? "请求服务超时" : "服务器内部错误";
                result.put("message", msg);
                return new ByteArrayInputStream(new ObjectMapper().writeValueAsString(result).getBytes());
            }
            @Override
            public HttpHeaders getHeaders() {
                HttpHeaders headers = new HttpHeaders();
                headers.setContentType(MediaType.APPLICATION_JSON);
                return headers;
            }
        };
    }
}

则当服务请求失败时,统一返回如下格式的响应

{
    "code": "500",
    "message": "服务器内部错误"
}

总结

本文主要对Zuul过滤器相关内容及自定义使用进行了介绍,同时对过滤器运行过程中异常的处理及服务调用失败的降级回调进行了简单说明。出于篇幅,开发过程中更具体的细节我们后续再继续探讨。认真生活,快乐分享欢迎关注微信公众号:空山新雨的技术空间获取Spring Boot,Spring Cloud, Docker等系列技术文章公众号二维码

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Spring Cloud(七):服务网关zuul过滤器 的相关文章

随机推荐

  • C语言——判断密码复杂程度

    题目 编写一个函数 传入密码 判断密码复杂程度 判断密码是否包含大写 小写 数字 解题思路 我们设定若包含大写 小写 数字其中一类为简单 包含两类为中等 三类为复杂 暂时不考虑其他字符的出现 我们预先设定密码为 Maobing520 主函数
  • layui分页遇到的问题及button和input的区别

    参考文献 漂亮分页样式和代码 http layui shagua wiki layuidoc demo laypage html 按钮input和代码 http www shagua wiki project 3 p 84
  • idea 运行vue项目,修改vue代码后,不起左右。删掉.idea重新导入就好了

    这是第二次修改vue代码后不起作用了 不知道idea2019怎么搞的 删掉 idea重新导入就好了 附 要vue 3 0及以上 才有public目录 2 x没有
  • C++-Z字扫描实现(Zigzag Scan)

    Z字扫描 Zigzag Scan 将二维矩阵压缩成行输出 int index 0 for int i 0 i
  • 使用FastGithub,告别无法访问Github

    FastGithub github定制版的dns服务 解析github最优的ip 源代码开源在github 加速原理 多种渠道获取github的ip github公开的ip 各dns服务器提值的ip ipaddress com反查的ip 轮
  • JavaScript的关键字详解

    1 abstract 抽象的 2 continue 用于跳过循环中的一个迭代 并继续执行循环中的下一个迭代 3 finally 在 try 和 catch 之后无论有无异常都会执行 4 instanceof 5 private 6 fals
  • Mac流程图工具-StarUML介绍

    转载请注明来源 作者 loongshawn http blog csdn net loongshawn article details 78786685 建议读者阅读原文 确保获得完整的信息 1 背景说明 推荐一款Mac流程图工具 效果如下
  • FIFO最小深度计算

    FIFO最小深度计算 文章目录 1 FIFO最小深度 2 示例分析 1 FIFO最小深度 在数据的传输中当读速率慢于写速率时 FIFO便可被用作系统中的缓冲元件或队列 类似于水流经过一个缓冲区 上游水流速度快 下游水流速度慢 部分水就可以被
  • 05-----关于C++使用VS时出现 error C2248: “xxx“ 无法访问private成员(在“MySpdlog“类中声明)

    一 问题描述 项目中遇到一下问题 需要写个测试程序 但是在搭建的过程中 出现错误的 心想 代码一样的 怎么就报错了呢 于是开始查找问题 错误如下 因为我的测试程序文件不算多 就5个 代码也不多 于是我使用软件进行对比正确的代码和错误的代码
  • uniapp 之 微信小程序、支付宝小程序 对于自定义导航栏的不同

    目录 前言 微信小程序 代码 支付宝小程序 首页配置文件 二级菜单页面 配置 总结 不同 相同 前言 小程序都是 uni app 写的 不是原生 微信小程序 代码 pages json文件中配置 重点 navigationStyle cus
  • 电磁兼容测试分析软件软件,电磁兼容测试方案的主要应用

    汽车制造商必须保证汽车的无用杂散处于规定界限范围内 EMI 同时 要保证汽车能够抵抗来自外部和内部源的骚扰 EMS 例如电视发射机 移动基站或其他电子汽车组件 作为该领域的全球领导者 罗德与施瓦茨提供高效的测试解决方案 确保您的产品符合电磁
  • 恢复hosts文件

    因破解GoLand需要 得修改hosts文件 打开目录 C Windows System32 drivers etc 尼玛 hosts文件怎么没有了 什么局面 手动恢复hosts文件 窗口键 X 运行 cmd打开控制台 输入命令 for f
  • MySQL的bin目录去哪里了

    方法一 点开始 运行 输入 services msc 在打开的 服务管理器 中找到mysql并双击 会弹出来个属性框框 常规 选项卡里面有个 执行路径 从这个路径你就可以看到它的bin目录在哪 很多人没有找到框 再次更新一下 方法二 打开任
  • 小米造车150天:烈火烹油第一枪

    贾浩楠 发自 凹非寺量子位 报道 公众号 QbitAI 小米官宣造车 小米开启智能驾驶招聘 小米7737万美元收购深动科技 小米汽车正式完成工商注册 落户北京亦庄 入局5个月 这是小米造车的一系列进展 还没算上2万多简历 300人团队 大量
  • IOS上架流程详解,包含审核避坑指南!

    准备 开发者账号 完工的项目 上架步骤 一 创建App ID 二 创建证书请求文件 CSR文件 三 创建发布证书 CER 四 创建Provisioning Profiles配置文件 PP文件 五 在App Store创建应用 六 打包上架
  • ABP框架 - 介绍

    文档目录 本节内容 简介 一个快速示例 其它特性 启动模板 如何使用 简介 我们总是对不同的需求开发不同的应用 但至少在某些层面上 一次又一次地重复实现通用的和类似的功能 如 授权 验证 异常处理 日志 本地化 数据库连接管理 设置管理 审
  • C++ 实现Kafka TLS双向加密

    C 实现Kafka TLS双向加密 基本概念 非对称加密 CA TLS双向加密过程 TLS 双向认证流程 代码 基本概念 Kafka TLS双向加密包含的知识涉及到对称加密 非对称加密 Kafka CA 数字证书 本文采用C librdka
  • (python代码+讲解)重叠社区发现EAGLE层次算法的实现

    EAGLE是一种基于最大团聚类的层次聚类算法 用来揭示网络的层次化和层次化社区结构 该算法采用一种聚类框架 来处理最大派系 1 算法的实现 首先用Bron Kerbosch算法找到网络中的最大派系 要设置一个阈值k来丢弃所有小于K的最大派系
  • RS232中RTS和CTS的作用

    RS232中RTS和CTS的作用 转载 2011年12月26日 23 21 06 1149 0 0 RS232中RTS和CTS的作用 问 以前挺明白的 今天一下子觉得以前的理解都不对了 以下三种解释哪个对呢 解释一 RTS 终端我已经准备就
  • Spring Cloud(七):服务网关zuul过滤器

    上文介绍了Zuul的基本使用与路由功能 本文接着介绍Zuul的核心概念 Zuul过滤器 filter Zuul的功能基本通过Zuul过滤器来实现 类比于Struts的拦截器 只是Struts拦截器用到责任链模式 Zuul则是通过Filter