Exception与Result的介绍(代码示例)

本篇文章给大家带来的内容是关于exception与result的介绍(代码示例),有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助。

分布式系统开发中,我们经常需要将各种各样的状态码、错误信息传递给最外层的调用方,这个调用方通常是http/api接口,错误信息比如登录失效、参数错误等等。

最外层接口暴露的数据通常是类似于{code, msg, data}这样的json格式,这一点没有任何争议。

但是分布式系统的节点之间RPC调用、节点内部方法调用中,通常会用ServiceException或Result的方式进行错误信息的传递,这两种方式有什么区别以及孰优孰劣呢?本文侧重于开发效率与系统性能探讨这个问题。

Result介绍

这是一种比较常见的错误信息传递方式,某些大厂甚至直接将它们设为技术规范,强制各个团队采用这种方式。常见的Result模板如下:

@Datapublic class Result {    private int code; // 也可以是String等    private String msg;    private T data;}

登录后复制

在系统开发中的应用通常是这样的:

Result userModelResult = userService.query(userId);if (!userModelResult.isSuccess() || userModelResult.getData != null) {    return Result.fail(userModelResult); // 透传错误}UserModel userModel = userModelResult.getData();if (userModel.getStatus() != UserStatusEnum.NORMAL) {    return Result.fail("user unavaliable"); // 用户不可用}// ... 正常使用UserModel

登录后复制

在比较复杂的分布式微服务环境中,类似的代码非常之多,每个依赖服务的调用都伴随着一段类似的容错逻辑。

这种模式比较类似Golang语言中的错误码处理,这也是Golang比较被人诟病的地方,即每一步都得进行错误判断。

更残酷的现实是,尽管有了Result封装,但是仍然会有后端系统的Exception透传过来。在我接触过的实际应用中,这种突破Result封装的异常透传绝非个例,我自己负责的系统在调用更后端的国内最强交易系统时,就曾接到过最内部交易中心TC的业务异常,排查问题时追踪的团队就有不止5个。

ServiceException介绍

顾名思义,这个方式就是使用异常中断将正常逻辑与异常逻辑进行拆分。

在系统开发中,大部分错误都需要直接中断服务,直接将错误反馈给用户,正因为如此,我们在使用Result时,经常需要写类似if(result.isFail()){return…}这样的代码。而使用ServiceException,我们就可以省略掉绝大部分类似的代码。

通常ServiceException可以这样定义:

@Getterpublic class ServiceException extends RuntimeException {    private final int code;    private final String msg;    public ApiException() {        this(-1, null);    }    public ApiException(Code code) {        this(code, null);    }    public ApiException(Code code, String msg) {        super(msg);        this.code = code;        this.msg = msg;    }}

登录后复制

系统内部组件在遇到数据缺失、越权访问、登录失效、账户锁定等异常情况时,直接抛出ServiceException中断逻辑,然后由最外层的Filter或Aspect捕捉异常,提取其中的code和msg返回给用户。

实际使用的代码逻辑类似这样:

UserModel userModel = userService.query(userId); // userID不存在、不可用等隐藏在异常中// ... 使用userModel

登录后复制

这种方式明显优雅、精简了许多,对于开发效率的提高以及后期维护都有帮助。

但是在坊间有许多流言声称,使用异常中断会影响性能,甚至有人通过简单的性能测试推出异常中断的性能耗时比返回Result快几百倍云云。

性能测试

针对性能问题,我也进行了一个简单的测试,具体测试代码参见:

https://github.com/sisyphsu/b…

这里使用JMH进行性能测试,说到benchmark,真的是羡慕golang语言自带的test库,实在是太方便了。

测试内部的业务逻辑非常简单,只是调用一次System.currentTimeMillis()并返回long时间戳。

性能测试中分别使用Result返回值以及抛出Exception,针对抛出异常的性能测试,又增加的不同深度的调用栈测试,这是因为Java在抛出异常时,需要分析当前Thread的栈,而调用栈越深,所造成的性能损耗就越大。具体栈深度取值为1、10、100:

Test.test                  avgt    5  0.027 ± 0.001  us/opTest.testException         avgt    5  1.060 ± 0.045  us/opTest.testDeep10Exception   avgt    5  1.826 ± 0.122  us/opTest.testDeep100Exception  avgt    5  9.802 ± 0.411  us/op

登录后复制

乍一看,异常栈深度为100的性能损耗确实是普通方法调用的360倍,有的人也确实是基于这种理由得出Java异常中断性能损耗严重的结论。

分析性能的影响

但是需要注意时间单位,只是微秒而已,毫秒的千分之一、秒的百万分之一。

假设某个微服务单CPU吞吐量为1000QPS,而其中有10%是非法请求,那么异常中断的性能损耗也只是万分之一而已,对于服务耗时的影响也只是0.001毫秒而已。

在性能测试中,业务耗时只是获取系统时间,大概耗时为25ns。正因为如此才显得异常中断的性能损耗达到恐怖的“几百倍”,但是如果业务耗时从25ns变为25us、25ms呢?

再谈性能瓶颈

我们在分析系统性能时,一定要搞清楚它的数量级以及性能瓶颈,切记陷入性能优化的困境。

举个粗糙例子,在常规服务中,利用了索引的DB操作在1~10毫秒之间,访问分布式Cache的耗时在3~30毫秒之间,微服务RPC的网络性能损耗在3~10毫秒之间,客户端与服务器之间的网络耗时在5~300毫秒之间,如此之类等等。在这种情况下,优化0.001毫秒的性能隐患无异于捡了芝麻丢了西瓜。

我曾经写过类似TCP的底层网络协议,在那种高频场景中,算法优化带来0.1微秒的性能优化就意味着每秒钟吞吐量几成甚至几倍的提升,但是在分布式调用的低频场景中,这种性能用处没有任何用处。

另外一个例子,几年前我和同事在讨论DB数据表设计时,因为订单状态使用什么长度的int而争执的面红脖子粗,现在想想,订单状态上优化的1个字节,长年累月下来也只是节省不到1MB的磁盘空间而已,有什么用呢?

RPC中的异常中断

对于使用Dubbo、HSF这种远程调用框架而言,使用异常中断进行错误信息传递,需要注意一点就是,异常类型需要设计为通用的,即各个微服务都引用的基础类型。

在某厂的技术规范中有说到:

1) 使用抛异常返回方式,调用方如果没有捕获到就会产生运行时错误。

2) 如果不加栈信息,只是new自定义异常,加入自己的理解的error message,对于调用端解决问题的帮助不会太多。如果加了栈信息,在频繁调用出错的情况下,数据序列化和传输的性能损耗也是问题。

我对这种技术规范相当的不以为然。

首先业务异常本来就需要调用方透传给最外层,诸如数据不存在、登录失效、用户锁定这种异常,中间的调用方捕获了也往往没有什么用。

其次又是鬼扯性能损耗,这种低频的数据序列化和内网传输会有什么样的性能损耗呢?栈信息透传给调用方也有益于故障排查,我曾经接到过TC的异常栈信息,根据栈中的package直接就绕过三四层找到了底层出错的地方,可以说是节省了大量的时间。

结论

在分布式微服务中,采用异常中断可以大幅精简业务代码,对于性能的影响也微乎其微。

辅助以@NotNull、@Nullable等注解,可以让分布式开发如风一般的快速便捷。在复杂的服务网络中,业务异常也可以方便开发人员精确地定位错误,避免大家顺着调用链一层一层地追踪故障点的尴尬情景。

以上就是Exception与Result的介绍(代码示例)的详细内容,更多请关注【创想鸟】其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至253000106@qq.com举报,一经查实,本站将立刻删除。

发布者:PHP中文网,转转请注明出处:https://www.chuangxiangniao.com/p/3044302.html

(0)
上一篇 2025年3月13日 15:16:03
下一篇 2025年2月18日 08:19:24

AD推荐 黄金广告位招租... 更多推荐

相关推荐

  • 怎么获取json中的数据

    json是首先一种数据结构,说白了就是对数据的描述,刚刚出现是为了取代xml,可惜并没有,但是在作为配置文件上,却是很好,由于它小巧灵活,描述数据很好,所以在网络上进行数据传输更加方便。 请记住json对数据的描述形式,既然是形式,那么它的…

    2025年3月13日
    200
  • java中怎么创建索引

    java中创建索引的方法:首先把对象转换为JSON字符串;然后把json文档写入索引;最后使用Java代码新建一个Java项目,在其中写好创建索引代码调用就可以了。 索引是与表或视图关联的磁盘上结构,可以加快从表或视图中检索行的速度。索引包…

    2025年3月13日
    200
  • jq是指什么?

    json 是一种轻量级的数据交换格式。jq 是一款命令行下处理 json 数据的工具。下面我将给大家详细介绍一下jq,感兴趣的朋友可以了解一下。 json采用完全独立于语言的文本格式,具有方便人阅读和编写,同时也易于机器的解析和生成。 这些…

    2025年3月13日
    200
  • Java中使用构造函数与使用setter的效率差别

    在对java代码进行优化的时候,想方设法的要提高整体的效率,使用jprofiler看代码的时间占比,然后,看看哪些部分是可以优化的,减少运行时间的。下面有这么几个方向。 1,能使用构造函数一步到位的,就尽量使用构造函数,而不是使用一个个se…

    2025年3月13日 编程技术
    200
  • java判断是否json格式

    java判断是否json格式 JsonObject和JsonArray对象都没有能快速判断json格式合法性的方法,只好使用捕获异常的方式判断json合法性。 代码如下: /** * 判断是JsonObject * @param obj *…

    2025年3月13日
    200
  • java读取json数据中文乱码解决

    java读取json数据出现乱码的代码:(推荐:java视频教程) //从json文件中读取数据StringBuffer stringBuffer = new StringBuffer();try {BufferedReader buffe…

    2025年3月13日
    200
  • 初次使用vscode如何编写第一个java程序

    准备工作: 1、安装扩展 2、配置java路径 左上角 文件-》首选项-》设置  打开setting.json,添加java.home 立即学习“Java免费学习笔记(深入)”; 最后重启即可。 第一个java程序 1、创建一个名为hell…

    2025年3月13日
    200
  • java乱码问号解决方法

    java乱码问号如下图: 所有涉及到汉字的,全变成了问号,奇怪的很,国际惯例,从html页面开始检查, 看着编码没问题啊,而且页面其他模块的汉字是可以正常显示的,那么开始断定是后台的问题,从action里开始看起,把request、resp…

    2025年3月13日 编程技术
    200
  • java返回乱码怎么办

    java返回乱码怎么办? java服务器返回中文乱码的解决办法 推荐教程:《java学习》 从java服务器中传来的中文显示为问号“?”,例如下面的name的值: 原因是服务器response的charset没有设置位“UTF-8&#824…

    2025年3月13日
    200
  • 安卓开发和Java开发有什么区别?

    安卓开发和Java开发有什么区别? Java是一种语言,以Java语言为基础的开发都可以是Java开发,Java开发包含的方面太多了,而安卓开发是Java开发的一个分支,安卓开发包括安卓应用开发,以及安卓系统的开发。 安卓开发的框架 1、B…

    2025年3月13日
    200

发表回复

登录后才能评论