Spring Cloud实战小贴士:Zuul统一异常处理(一)

在上一篇《Spring Cloud源码分析(四)Zuul:核心过滤器》一文中,我们详细介绍了Spring Cloud Zuul中自己实现的一些核心过滤器,以及这些过滤器在请求生命周期中的不同作用。我们会发现在这些核心过滤器中并没有实现error阶段的过滤器。那么这些过滤器可以用来做什么呢?接下来,本文将介绍如何利用error过滤器来实现统一的异常处理。

过滤器中抛出异常的问题

首先,我们可以来看看默认情况下,过滤器中抛出异常Spring Cloud Zuul会发生什么现象。我们创建一个pre类型的过滤器,并在该过滤器的run方法实现中抛出一个异常。比如下面的实现,在run方法中调用的doSomething方法将抛出RuntimeException异常。

public class ThrowExceptionFilter extends ZuulFilter {

private static Logger log = LoggerFactory.getLogger(ThrowExceptionFilter.class);

@Override
public String filterType() {
return "pre";
}

@Override
public int filterOrder() {
return 0;
}

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

@Override
public Object run() {
log.info("This is a pre filter, it will throw a RuntimeException");
doSomething();
return null;
}

private void doSomething() {
throw new RuntimeException("Exist some errors...");
}

}

运行网关程序并访问某个路由请求,此时我们会发现:在API网关服务的控制台中输出了ThrowExceptionFilter的过滤逻辑中的日志信息,但是并没有输出任何异常信息,同时发起的请求也没有获得任何响应结果。为什么会出现这样的情况呢?我们又该如何在过滤器中处理异常呢?

解决方案一:严格的try-catch处理

回想一下,我们在上一节中介绍的所有核心过滤器,是否还记得有一个post过滤器SendErrorFilter是用来处理异常信息的?根据正常的处理流程,该过滤器会处理异常信息,那么这里没有出现任何异常信息说明很有可能就是这个过滤器没有被执行。所以,我们不妨来详细看看SendErrorFiltershouldFilter函数:

public boolean shouldFilter() {
RequestContext ctx = RequestContext.getCurrentContext();
return ctx.containsKey("error.status_code") && !ctx.getBoolean(SEND_ERROR_FILTER_RAN, false);
}

可以看到该方法的返回值中有一个重要的判断依据ctx.containsKey("error.status_code"),也就是说请求上下文中必须有error.status_code参数,我们实现的ThrowExceptionFilter中并没有设置这个参数,所以自然不会进入SendErrorFilter过滤器的处理逻辑。那么我们要如何用这个参数呢?我们可以看一下route类型的几个过滤器,由于这些过滤器会对外发起请求,所以肯定会有异常需要处理,比如RibbonRoutingFilterrun方法实现如下:

public Object run() {
RequestContext context = RequestContext.getCurrentContext();
this.helper.addIgnoredHeaders();
try {
RibbonCommandContext commandContext = buildCommandContext(context);
ClientHttpResponse response = forward(commandContext);
setResponse(response);
return response;
}
catch (ZuulException ex) {
context.set(ERROR_STATUS_CODE, ex.nStatusCode);
context.set("error.message", ex.errorCause);
context.set("error.exception", ex);
}
catch (Exception ex) {
context.set("error.status_code", HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
context.set("error.exception", ex);
}
return null;
}

可以看到,整个发起请求的逻辑都采用了try-catch块处理。在catch异常的处理逻辑中并没有做任何输出操作,而是往请求上下文中添加一些error相关的参数,主要有下面三个参数:

  • error.status_code:错误编码
  • error.exceptionException异常对象
  • error.message:错误信息

其中,error.status_code参数就是SendErrorFilter过滤器用来判断是否需要执行的重要参数。分析到这里,实现异常处理的大致思路就开始明朗了,我们可以参考RibbonRoutingFilter的实现对ThrowExceptionFilterrun方法做一些异常处理的改造,具体如下:

public Object run() {
log.info("This is a pre filter, it will throw a RuntimeException");
RequestContext ctx = RequestContext.getCurrentContext();
try {
doSomething();
} catch (Exception e) {
ctx.set("error.status_code", HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
ctx.set("error.exception", e);
}
return null;
}

通过上面的改造之后,我们再尝试访问之前的接口,这个时候我们可以得到如下响应内容:

{
"timestamp": 1481674980376,
"status": 500,
"error": "Internal Server Error",
"exception": "java.lang.RuntimeException",
"message": "Exist some errors..."
}

此时,我们的异常信息已经被SendErrorFilter过滤器正常处理并返回给客户端了,同时在网关的控制台中也输出了异常信息。从返回的响应信息中,我们可以看到几个我们之前设置在请求上下文中的内容,它们的对应关系如下:

  • status:对应error.status_code参数的值
  • exception:对应error.exception参数中Exception的类型
  • message:对应error.exception参数中Exceptionmessage信息。对于message的信息,我们在过滤器中还可以通过ctx.set("error.message", "自定义异常消息");来定义更友好的错误信息。SendErrorFilter会优先取error.message来作为返回的message内容,如果没有的话才会使用Exception中的message信息

解决方案二:ErrorFilter处理

通过上面的分析与实验,我们已经知道如何在过滤器中正确的处理异常,让错误信息能够顺利地流转到后续的SendErrorFilter过滤器来组织和输出。但是,即使我们不断强调要在过滤器中使用try-catch来处理业务逻辑并往请求上下文添加异常信息,但是不可控的人为因素、意料之外的程序因素等,依然会使得一些异常从过滤器中抛出,对于意外抛出的异常又会导致没有控制台输出也没有任何响应信息的情况出现,那么是否有什么好的方法来为这些异常做一个统一的处理呢?

这个时候,我们就可以用到error类型的过滤器了。由于在请求生命周期的preroutepost三个阶段中有异常抛出的时候都会进入error阶段的处理,所以我们可以通过创建一个error类型的过滤器来捕获这些异常信息,并根据这些异常信息在请求上下文中注入需要返回给客户端的错误描述,这里我们可以直接沿用在try-catch处理异常信息时用的那些error参数,这样就可以让这些信息被SendErrorFilter捕获并组织成消息响应返回给客户端。比如,下面的代码就实现了这里所描述的一个过滤器:

public class ErrorFilter extends ZuulFilter {

Logger log = LoggerFactory.getLogger(ErrorFilter.class);

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

@Override
public int filterOrder() {
return 10;
}

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

@Override
public Object run() {
RequestContext ctx = RequestContext.getCurrentContext();
Throwable throwable = ctx.getThrowable();
log.error("this is a ErrorFilter : {}", throwable.getCause().getMessage());
ctx.set("error.status_code", HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
ctx.set("error.exception", throwable.getCause());
return null;
}

}

在将该过滤器加入到我们的API网关服务之后,我们可以尝试使用之前介绍try-catch处理时实现的ThrowExceptionFilter(不包含异常处理机制的代码),让该过滤器能够抛出异常。这个时候我们再通过API网关来访问服务接口。此时,我们就可以在控制台中看到ThrowExceptionFilter过滤器抛出的异常信息,并且请求响应中也能获得如下的错误信息内容,而不是什么信息都没有的情况了。

{
"timestamp": 1481674993561,
"status": 500,
"error": "Internal Server Error",
"exception": "java.lang.RuntimeException",
"message": "Exist some errors..."
}

本文节选自《Spring Cloud微服务实战》,部分稍做加工,转载请注明出处


相关阅读

  • 《Spring Cloud实战小贴士:Zuul统一异常处理(二)》
  • 《Spring Cloud源码分析(四)Zuul:核心过滤器》
  • 《Spring Cloud实战小贴士:Zuul处理Cookie和重定向》
  • 《Spring Cloud构建微服务架构(五)服务网关》

money.jpg

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/477476.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ACL’22 | 为大模型定制的数据增强方法FlipDA,屠榜六大NLU 数据集

本文转载自公众号“夕小瑶的卖萌屋”,专业带逛互联网算法圈的神操作 -----》我是传送门 关注后,回复以下口令: 回复【789】 :领取深度学习全栈手册(含NLP、CV海量综述、必刷论文解读) 回复【入群】&#xf…

技术动态 | 面向可解释性的知识图谱推理研究

导读:本次演讲的主题是面向可解释性的知识图谱推理研究,报告分为以下 5 个部分:研究背景前沿进展研究动机近期研究研究展望分享嘉宾|万国佳 武汉大学 计算机学院 博士后编辑整理|xiaomei出品平台|DataFunTa…

LeetCode 1332. 删除回文子序列

1. 题目 给你一个字符串 s,它仅由字母 ‘a’ 和 ‘b’ 组成。每一次删除操作都可以从 s 中删除一个回文 子序列。 返回删除给定字符串中所有字符(字符串为空)的最小删除次数。 「子序列」定义:如果一个字符串可以通过删除原字符…

Spring Cloud源码分析(四)Zuul:核心过滤器

通过之前发布的《Spring Cloud构建微服务架构(五)服务网关》一文,相信大家对于Spring Cloud Zuul已经有了一个基础的认识。通过前文的介绍,我们对于Zuul的第一印象通常是这样的:它包含了对请求的路由和过滤两个功能&am…

预训练再次跨界!百度提出ERNIE-GeoL,地理位置-语言联合预训练!

源 | 百度NLP本文介绍『文心大模型』的一项最新工作:“地理位置-语言”预训练模型ERNIE-GeoL。论文链接:https://arxiv.org/abs/2203.09127实践中的观察近年来,预训练模型在自然语言处理、视觉等多个领域都取得了显著效果。基于预训练模型&am…

LeetCode 1333. 餐厅过滤器(Lambda排序)

1. 题目 给你一个餐馆信息数组 restaurants,其中 restaurants[i] [idi, ratingi, veganFriendlyi, pricei, distancei]。你必须使用以下三个过滤器来过滤这些餐馆信息。 其中素食者友好过滤器 veganFriendly 的值可以为 true 或者 false,如果为 true …

Spring Cloud实战小贴士:Zuul处理Cookie和重定向

由于我们在之前所有的入门教程中,对于HTTP请求都采用了简单的接口实现。而实际使用过程中,我们的HTTP请求要复杂的多,比如当我们将Spring Cloud Zuul作为API网关接入网站类应用时,往往都会碰到下面这两个非常常见的问题&#xff1…

论文浅尝 | Language Models (Mostly) Know What They Know

笔记整理:程思源、梁孝转,浙江大学在读硕士,研究方向为知识图谱的表示学习,自然语言处理,预训练对于一个语言模型,我们最终希望得到一个“诚实”的人工智能系统,即语言模型需要准确并且忠实地评…

百度AI技术盛宴来了!大咖齐聚解读CV/NLP/跨模态大模型技术!

随着人工智能步入工业大生产阶段,AI大模型正在加速走出实验室,在全球范围内逐步实现产业落地应用的突破。自2020年至今,越来越多的科技巨头和科研机构参与其中。去年12月,百度发布了全球首个知识增强千亿级大模型——鹏城-百度文心…

Spring Cloud实战小贴士:健康检查

今天在博客的交流区收到一条不错的问题,拿出来给大家分享一下。具体问题如下: 因为项目里面用到了redis集群,但并不是用spring boot的配置方式,启动后项目健康检查老是检查redis的时候状态为down,导致注册到eureka后项…

恕我直言,你的模型可能并没看懂 prompt 在说啥

本文转载自公众号“夕小瑶的卖萌屋”,专业带逛互联网算法圈的神操作 -----》我是传送门 关注后,回复以下口令: 回复【789】 :领取深度学习全栈手册(含NLP、CV海量综述、必刷论文解读) 回复【入群】&#xf…

开源开放 | 区域供冷供热系统及空调系统知识图谱

OpenKG地址:http://openkg.cn/dataset/less开放许可协议:CC BY-SA 4.0 (署名相似共享)贡献者:浙江大学(赵阳,李婷婷,章超波)1、背景区域供冷供热系统及空调系统领域涉及知…

LeetCode 1334. 阈值距离内邻居最少的城市(最短路径Dijkstra)

1. 题目 有 n 个城市,按从 0 到 n-1 编号。给你一个边数组 edges,其中 edges[i] [fromi, toi, weighti] 代表 fromi 和 toi 两个城市之间的双向加权边,距离阈值是一个整数 distanceThreshold。 返回能通过某些路径到达其他城市数目最少、且…

五个同事想计算他们的平均工资,但公司不让吐露薪资,如何实现?

源 | Xpecya知乎大家好我是卖萌酱。昨天在知乎上刷到一个很有意思的问题:“五个同事决定计算他们的平均工资,在大家互相不告诉薪水的情况下,如何才能做到这一点?”。确实互联网公司是不让员工讨论薪资的,但通过一些神操…

基于Consul的分布式信号量实现

本文将继续讨论基于Consul的分布式锁实现。信号量是我们在实现并发控制时会经常使用的手段,主要用来限制同时并发线程或进程的数量,比如:Zuul默认情况下就使用信号量来限制每个路由的并发数,以实现不同路由间的资源隔离。 信号量(…

图谱实战 | 图视角下的信息抽取技术研究

导读:本次分享题目为《图视角下的信息抽取技术研究》,主要介绍:研究背景和意义国内外研究现状研究目标与内容主要成果与创新之处完成项目及发表论文情况分享嘉宾|郁博文博士 达摩院 算法专家编辑整理|王露出品平台&…

LeetCode 1335. 工作计划的最低难度(DP)

1. 题目 你需要制定一份 d 天的工作计划表。工作之间存在依赖&#xff0c;要想执行第 i 项工作&#xff0c;你必须完成全部 j 项工作&#xff08; 0 < j < i&#xff09;。 你每天 至少 需要完成一项任务。工作计划的总难度是这 d 天每一天的难度之和&#xff0c;而一天…

CCKS-面向数字商务的知识图谱比赛重磅上线,奖金等你来拿!

​CCKS 2022面向数字商务的知识图谱评测赛题介绍阿里巴巴商品数据规模庞大&#xff0c;商品知识图谱为海量异构的商品数据的组织、管理和利用提供了有效的方式。商品数据模态丰富&#xff0c;动态性高&#xff0c;数据存在噪声&#xff0c;这些都对商品的认知和理解带来了巨大挑…

基于Consul的分布式锁实现

我们在构建分布式系统的时候&#xff0c;经常需要控制对共享资源的互斥访问。这个时候我们就涉及到分布式锁&#xff08;也称为全局锁&#xff09;的实现&#xff0c;基于目前的各种工具&#xff0c;我们已经有了大量的实现方式&#xff0c;比如&#xff1a;基于Redis的实现、基…

LeetCode 1289. 下降路径最小和 II(DP)

1. 题目 给你一个整数方阵 arr &#xff0c;定义「非零偏移下降路径」为&#xff1a;从 arr 数组中的每一行选择一个数字&#xff0c;且按顺序选出来的数字中&#xff0c;相邻数字不在原数组的同一列。 请你返回非零偏移下降路径数字和的最小值。 示例 1&#xff1a; 输入&a…