• 错误码


    关于错误码的那点事 - 知乎 https://zhuanlan.zhihu.com/p/411726319

    关于错误码的那点事

     

    一、什么是错误码

    错误码一般情况分为对外错误码,系统内部错误码。

    对外错误码常应用在一些开放接口,比如http接口,rpc接口等,通过错误码的形式给予上游更加友好的错误提示以及错误描述。

    系统内部错误码,存在于关系紧密的微服务之间、或者程序的上下游中。因为某种业务错误、或者系统不可用造成的错误,开发人员可以根据错误码、错误信息进行具体定位;或者根据上游根据错误码做业务逻辑判断,从而保证整体流程完整性。

    总而言之,错误码的作用: 指出错误的原因,快速定位问题,指导上游系统做出正确的业务判断,引导用户进行正确的操作。因此构建一个通用且架构清晰的错误码体系是一件很有必要的事情。

    那么怎么定义一种对外对内都友好的错误码呢?至今业内也并没有一个比较好的方案或者规范。这里结合新老支付系统融合,逐步进行摸索。

    二、支付系统错误码现状

    由于历史原因,公司内部目前有两套运行中的钱包系统。为了提高钱包系统可用性以及性能,对两套钱包系统进行迁移整合升级优化。但迁移合并两套钱包系统的途中,发现两套钱包系统、以及之前现存的已经优化过的错误码之前存在冲突、类型不一致、定义混乱、随意性高等问题。

    由于其一钱包系统之前由其他团队开发维护,后期也对其进行重构过,这造成了新钱包系统的错误码定义规则存在两套;从代码角度来看,新老钱包系统在设计之初的时候,对错误码的定义都各自定义了一个比较合适的规范,但是在后期开发维护中,越来越少的开发人员去遵循规范定义错误码,最终造成了由错误描述决定错误码的现象。而目前新系统中错误码,在设计之初,并未考虑到未来整合带来的错误码冲突等问题,造成了部分错误码重合,语义大相径庭的问题,所以重新定义错误码规范也是迫在眉睫。

    钱包系统错误码现状:

    钱包一错误码定义: 6位错误码。 首位表示错误类型,区分系统级别、校验、rpc服务调用错误。 这种设计方式,第一位是明确的,后面5位都是预留的错误码,长度足够,满足后续错误码的增加;缺点错误码对应的类型太少,会出现相同语义的错误码,对应不同的首位数字。后重构版本的错误码修改了原有的错误码位数,修改后7位。造成了该系统的接口层,6位错误码,7位错误码混乱,并且错误码没有分类,全部顺序后排。

    钱包二错误码定义: 提供错误码工具类,规定了大多数钱包常用的错误码;然后以此为基础,进行增加。优点: 错误码分类清晰,结构明了,缺点: 不容易根据错误码定位具体错误。

    简单来说,目前钱包系统错误码存在以下问题

    1. 错误码字段类型定义不一致。有的定义数值类型,有的定义字符串类型

    2.错误码重合问题。相同的错误码,在不同系统中有着不同的语义

    三、调研业内接口定义

    鉴于当前支付系统错误码的痛点,如错误码应该用数值类型还是字符串类型,长度命名格式是怎样的等问题,调研了多家大厂的API规范以及接口定义,来探索适用于支付系统的错误码规范。

    1. 微信支付

    a. 参考微信支付v2接口

    微信支付v2接口: 协议:http, content-type: text/xml

    参考链接: 

    原格式是xml,为了更加直观,这里先加工为json格式
    {
        "return_code":"SUCCESS",  //  SUCCESS/FAIL 此字段是通信标识,非交易标识,交易是否成功需要查看result_code来判断
        "return_msg":"OK",
        "result_code":"SUCCESS", // SUCCESS/FAIL  标识业务成功失败
        "err_code":"SYSTEMERROR", // 当result_code为FAIL的时候,该值返回业务错误码
        "err_code_des":"系统错误"
    }

    微信支付错误码结构为三级结构:

    一级、公共错误码(网关层) 。该层仅返回通信成功失败信息

    二级、业务错误码 (总): 表示该业务是否处理成功

    三级、具体业务错误码

    业务错误码举例:

    error_codeerr_code_des
    NOAUTH 商户无此接口权限
    INVALID_REQUEST 参数错误
    NOTENOUGH 余额不足

    b.参考微信支付v3接口

    微信支付v3接口: 协议:http, content-type: application/json

    参考链接: 

    err_codehttp_codeerr_msg
    USERPAYING 202 用户正在付款中
    OUT_TRADE_NO_USED 403 商户订单号重复
    ORDERNOTEXIST 404 订单不存在

    根据微信v3的接口文档可知,微信支付返回的业务错误的同时,会返回一个与之对应的httpcode。当http_code的状态码在[200,300)之间,认为该请求是有合法的返回的;当大于300时,判断接口返回一定是有异常错误的。微信支付V3 sdk封装了http_code与err_code的相关处理。

    对外暴露http接口,在抛出业务错误的同时,也要抛出相同语义的httpcode,这就需要开发人员明确httpcode语义。 这样的劣势在于学习成本较高,依赖开发人员对httpcode的熟练程度,可能会出现 httpcode语义与业务错误语义不一致的现象。

    2.支付宝错误码定义

    参考链接: 

    不同业务的业务错误码: 举一个接口的例子 

    sub_code、sub_msg这两个参数标识支付宝返回的业务错误码、业务错误信息;

    {
        "code":"",//网关返回码
        "msg":"",//网关返回码
        "sub_code":"ACQ.INVALID_PARAMETER",
        "sub_msg":"参数无效"
    }

    支付宝的错误码定义 与 微信支付的v2接口定义风格比较相像。

    支付宝错误码结构分为两级: 一级: 网关, 二级: 业务错误码。

    请求支付宝接口,先通过支付宝网关系统,网关系统进行验签、加解密、流控等功能,如果网关校验出错,则抛出公共错误码。网关校验成功之后,交给下游业务系统,sub_code都是语义明确的错误码,以及错误描述。

    3. google Api规范

    参考链接: 

    google的错误码定义中,将返回码的结构定义为

    message Status {
      // A simple error code that can be easily handled by the client. The
      // actual error code is defined by `google.rpc.Code`.
      int32 code = 1;
      // A developer-facing human-readable error message in English. It should
      // both explain the error and offer an actionable resolution to it.
      string message = 2;
      // Additional error information that the client code can use to handle
      // the error, such as retry delay or a help link.
      repeated google.protobuf.Any details = 3;
    }

    其中 code: 是错误码,message: 是具体的错误信息,detail是根据这个错误,推荐调用方采取怎样的措施。

    google对于错误码的定义,是比较简洁的。一个大类分配一个code;并不会因为多个相似的错误类型提供多个code。

    google规范里details信息: 表示该错误的具体原因,定义参考: 

    detail里面定义了 retryable信息 可以表示该错误码返回是可以进行重试的,并且给出推荐的重试延迟时间、QuotaFailure信息表示配额出错、限流超限等;badRequest可以详细的给出为什么会报这种错误 等等开发人员可以根据这样详细的错误返回码作出正确的反应。

    4. 微博 规范

    参考api: 

    {
        "request" : "/statuses/home_timeline.json",
        "error_code" : "20502",
        "error" : "Need you follow uid."
    }

    20502 其中 错误码的组成: 1位 错误级别编号(系统、服务) + 2位服务模块(比如 网关、微博、评价、私信 比较像服务标识) + 2位 错误代码(自定义的错误编码)

    20502
    服务级错误(1为系统级错误) 服务模块代码 具体错误代码

    微博的错误码有明确的结构语义。将服务系统标识,显示在错误码中。这个操作与支付宝错误码构成有一点相像。第一位 标识服务级、系统级的字段,不确定是否是表示该错误是有网关抛出、还是说明确几种系统级别的错误,按分类抛出。

    5.阿里巴巴的JAVA技术手册

    阿里巴巴规范

    第一点: 说明了错误码的特点: 要简单明了;

    第二点: 错误码最好定义为string字符串类型: 来源 + 错误编号 (这样,错误码的数值可以携带更多的信息)

    第三点: 避免随意添加错误码、避免直接暴露错误码给到用户侧

    综上所述, 通过调研的这几家的对外文档来看,错误码的定义业内并没有一个统一的规范。但是大体的设计思路如下:

    a. 错误码类型为字符串类型

    b. 系统如果由网关→ 内部服务构成,则错误码分为两级

    c. 错误码可以标识出抛出错误的来源服务。

    d. 错误码可以抽象出来两种 公共错误码、业务错误码

    四、思考与结论

    结合上述调研的行业错误码定义,以及当前钱包系统现状。由于支付系统处于整体业务流程的最基础层,提供支付、付款等RPC能力,不存在直接对外暴露http接口的可能。所以微信支付、支付宝支付的三层错误码结构并不适用于钱包系统。

    借鉴上述调研的api,错误码定义成字符串类型,更适用于业务场景,方便于后期的业务扩展。而google规范中对于错误码场景定义的统一规范,在一定程度上又降低了开发人员随意定义新错误码的可能。所以在error_code的场景定义上参考google-api规范

    错误码error_code: 字符串. 前N位为当前业务所属领域标识,优势: 易于区分其他业务错误码,如果后期由于业务扩展、或者业务缩小带来的服务拆分合并,错误码仍然可以保持当前的设置。

    提取公共常用的错误:

    参考google规范进行归集: 

    RPC接口常用错误error_code
    内部错误 115
    该请求不支持 112
    状态错误 110
    请求频繁 109
    没有权限类错误 108
    权限校验错误 107
    已存在类错误 106
    不存在类错误 105
    超时类错误 104
    参数错误 103
    未知错误
    (比如调用下游接口出错,可以抛这个异常)
    101

    其他业务错误码,可以使用200~999错误段进行自定义设置;但是如果有错误语义命中上述错误,则需要优先选择上述错误码。

    五、RPC错误码结构定义

    exception RpcError{
        1:required string err_code;
        2:required string err_desc;
    }
    1. 错误码定义:

    构成: 业务+错误码类型+自定义业务编码; 其中自定义业务编码是系统自定义的。

    标识处理业务错误码类型(3)自定义业务编码(2)
    参数校验失败:
    {
        "err_code":"WORDER.10501",
        "err_desc":"交易不存在"
    }
    WORDER:表示当前错误发生时,所处理的业务标识
    105: 不存在
    01: 交易不存在
    02: 用户不存在
    03: 订单不存在
    ...

    2. err_desc: 错误信息

    错误信息 开发人员可以快速定位问题。

    3. 错误要抛出来

    接口如果处理出错了,包装好合适的错误码以及错误描述,将该错误throw出,而不是将错误包在接口返回参数中。由于公司使用的是thrift协议-http,并且监控告警强依赖于httpCode,将错误直接抛出去,可以使监控更加有效的监控RPC接口,避免处理出错,但是返回httpcode是200的场景。

    六、网关类型的httpcode设计

    1. httpcode基础
    错误码代表含义
    2xx 成功
    3xx 重定向
    4xx 客户端原因引起的错误
    5xx 服务器原因引起的错误

    2. http服务返回

    {
        "code":"0", // 成功:0  失败:返回对应错误码
        "message":"",
        "data":{ //接口实际处理结果
     
        },
        "pagination":{
            "is_end":false,
            "is_first":true,
            "offset":20,
            "limit":20,
            "total":1000
        }
    }

    3 错误信息转换

    http接口,如果接口返回成功。则httpcode错误码返回200;如果失败,可以按需返回上述httpcode。

    http接口一般分为两种,第一种: 内部http接口; 第二种,与前端进行交互。内部http接口,错误码可以参照rpc接口;外部接口,避免将内部错误码外露出去,对用户展示的错误描述,最好可以在http层进行转换,不要将内部错误描述直接暴露出去。

    七、展望

    在系统迁移、重构、优化的时候,经常会遇到由于原有系统设计不合理,后续赶时间堆需求,造成的系统日益难以维护的问题。本次主要针对迁移过程中,遇到的错误码定义混乱这一问题,提出调研、以及自己的思考。目标能够统一错误码格式的规范,以及rpc、http类型接口的错误定义规范。在后续系统迁移过程中,使用规范的错误定义,降低上游系统理解错误的复杂度,并且在一定程度上可以降低运维效率。

    在后续的规划中,针对规范的错误码使用,可以有更多的技术设想,比如错误集成SDK,内部包含错误码定义,以及错误的打点上报,错误监控大盘等。

    发布于 2021-09-24 15:28
  • 相关阅读:
    解决:oracle+myBatis ResultMap 类型为 map 时,表字段类型有 Long/Blob/Clob 时报错
    总结:独立开发 jar 包组件——功能主要是支持查询数据库的所有表数据
    解决 iframe 后退不是主页面后退(浏览器 history)问题
    解决访问 jar 包里面的字体报错:OTS parsing error: incorrect file size in WOFF header
    html 如何访问 jar 包里面的静态资源(js、css、字体等)
    css3 实现打字机效果
    js 图形验证码
    input 设置 flex:1不起作用
    vue 样式加scoped不起作用
    node-mongoose开发中常见警告或问题-持续更新
  • 原文地址:https://www.cnblogs.com/rsapaper/p/15897258.html
Copyright © 2020-2023  润新知