写点什么

云 API 错误码的设计规则

作者:EquatorCoco
  • 2024-01-30
    福建
  • 本文字数:3625 字

    阅读完需:约 12 分钟

腾讯云云 API 错误码分为两级。以点号分隔。


第一级错误码统一由 API 平台提供 ,业务选择合适的错误场景。第二级错误码可选,业务可自定义。


例如,InvalidParameter.InvalidUserName。其中, 第一级错误码为InvalidParameter,表示这是一个参数错误。第二级错误码为InvalidUserName,表示具体错误的原因是 UserName 非法。


第一级错误码被整个平台进行监控。并进行错误统计,以及告警。第二级错误码则一般为业务自定义具体错误码。用来提供 FAQ,或者给上游服务更加明确地指示。对于错误码的设计,我非常不喜欢其他项目中设计为数字的形式。类似 100001,100002 这样的错误码,每次这样都得进行一次大脑的映射。


详细设计请看 luke的github仓库


如果有更好地设计,欢迎和我探讨。


举例


参数错误


比如InvalidParameterValue.InvalidAppId一级错误码为:InvalidParameterValue 定义为参数错误。二级错误码为:InvalidAppId 业务自定义,表示非法的应用 Id。


服务端错误


比如InternalError.CantFindConsul一级错误码为:InternalError 定义为系统错误,或者说服务端错误。二级错误码为:CantFindConsul 具体定义,表示 Consul 找不到。


这样的错误码,即使我不去看任何文档,我也能第一时间大概明白发生了什么。


处理云 API 的错误码类和异常类


如何抛出异常以及如何处理异常是一门学问。


常见的错误处理方式


1. 依据返回值


经常写 go 的同学比较熟悉。


public class HandleErrorTest {      Integer shouldPrint(String input){          if(input == null) {              return -1;          }          System.out.println(input);          return 0;      }        @Test      public void handleErrorByReturnValueIs0(){          Assert.assertTrue(0 == shouldPrint("123"));      }        @Test      public void handleErrorByReturnValueIs1(){          Assert.assertTrue(-1 == shouldPrint(null));      }  }
复制代码


什么时候需要使用这样的方式呢?


大多数业务中,我们不会采取这样的方式。只有一种场景:就是我们发现这个函数运行错误的时候,我们需要根据返回值处理,并继续处理下面的逻辑。例如这样。


public void handleErrorDemo(String x){      Integer retCode = shouldPrint(x);      if(retCode == -1){          handleNullCondition();      }else{          handleOtherCondition();      }  }
复制代码


返回错误值的本质是为了让我们根据错误值可以按照不同逻辑处理代码。


但是有个重大缺陷,就是在一些列函数调用中,必须依次返回这个错误。


public void func1(String x) int{      return func2(x);}
public void func2(String x) int{ return shouldPrint(x);}
复制代码


在 Go 代码中,我们可以看到 error。所以采用此种方式返回。但是在Java代码中,强烈不建议这么做。


2. 根据异常处理


Integer shouldPrint2(String input){      if(input == null) {          throw new RuntimeException("input can't be null");      }      System.out.println(input);      return 0;  }
复制代码


一般这样的写法,我在调用侧都不会做任何处理。直接让请求失败即可。


总结


其实大部分的异常情况,我的建议是直接抛出错误。不要吞掉异常!!!少部分带有逻辑的情况,可能需要按照返回错误值的方式来处理。


错误码类的设计


在做任何类设计的时候,我们都是有目标的,即要解决什么问题。这里先列出问题。


  1. 错误码需要区分一级错误码和二级错误码,可以很方便地帮我获取一级错误码。场景:对于上游业务来说,对于参数错误我会忽略,对于内部错误我会告警。


  1. 错误码可以返回更加定制化的信息。比如 ResourceNotFound,最好告诉我 ResourceId 是什么。


  1. 当链路中存在 2 个以上服务时,我想知道更下游的错误信息。


按照上述目标我们进行设计。


public interface IBizErrorCode {        String getErrorMessage(Object... args);      String getErrorCode();      PrimaryErrorCode getPrimaryCode();      String detailErrorCode();        String getDownStreamErrorMessage(Object... args);      String getDownStreamErrorCode(Object... args);      String getDownStreamPrimaryCode(String downStreamErrorCode);  }
复制代码


具体的实现例子如下


  public enum TestErrorCode implements IBizErrorCode {  	TestBaseError(PrimaryErrorCode.InternalError, "TestBaseError", "this is a message, reason = {0}", "{1}", "{2}"),      ResourceNotFound(PrimaryErrorCode.ResourceNotFound, "UserNotFound", "userId={0} not found", "{1}", "{2}")  ;    private final PrimaryErrorCode primaryErrorCode;      private final String detailErrorCode;      private final String errorMessage;      private final String downStreamErrorCode;      private final String downStreamErrorMessage;        TestErrorCode(PrimaryErrorCode primaryErrorCode, String detailErrorCode, String errorMessage,              String downStreamErrorCode, String downStreamErrorMessage) {          this.primaryErrorCode = primaryErrorCode;          this.detailErrorCode = detailErrorCode;          this.errorMessage = errorMessage;          this.downStreamErrorCode = downStreamErrorCode;          this.downStreamErrorMessage = downStreamErrorMessage;      }        @Override      public String getErrorMessage(Object... args) {          return new MessageFormat(this.errorMessage).format(args);      }        @Override      public String getErrorCode() {          Objects.requireNonNull(getPrimaryCode(), "primaryCode can't be null");          String code = getPrimaryCode().getCode();          if (StringUtils.isBlank(detailErrorCode())) {              return code;          } else {              StringBuffer stringBuffer = new StringBuffer();              stringBuffer.append(code)                      .append(".")                      .append(detailErrorCode());              return stringBuffer.toString();          }      }        @Override      public PrimaryErrorCode getPrimaryCode() {          return this.primaryErrorCode;      }        @Override      public String detailErrorCode() {          return this.detailErrorCode;      }        @Override      public String getDownStreamErrorMessage(Object... args) {          return replacePlaceholderWithValue(this.downStreamErrorMessage, args);      }        @Override      public String getDownStreamErrorCode(Object... args) {          return replacePlaceholderWithValue(this.downStreamErrorCode, args);      }        @Override      public String getDownStreamPrimaryCode(String downStreamErrorCode) {          return ErrorCodeUtils.getFirstCode(downStreamErrorCode);      }  }
复制代码


具体的使用场景如下


public void userNotFound(String userId){      if(StringUtils.isBlank(userId)){          throw new BizException(TestErrorCode.InvalidParameter, "userId can't be blank");      }      if(StringUtils.equals(userId, "secret user id")){          // do nothing      }else{          throw new BizException(TestErrorCode.UserNotFound, userId);      }  }    @Test  public void testUserNotFound() {      try{          userNotFound("luke");      }catch (BizException bizException){          bizException.printStackTrace();      }  }
复制代码


一级错误码的设计


云 API 因为分为一级错误码和二级错误码的概念。我们从规范可以看到,一级错误码的变化很少。直接设计为枚举类。


public enum PrimaryErrorCode {      AuthFailure("AuthFailure"),      FailedOperation("FailedOperation"),      InternalError("InternalError"),      ResourceInsufficient("ResourceInsufficient"),      ResourceNotFound("ResourceNotFound"),      /**    省略部分    **/    UnsupportedOperation("UnsupportedOperation"),      ;        PrimaryErrorCode(String code){          this.code = code;      }        public String getCode(){          return this.code;      }        private String code;  }
复制代码


二级错误码的设计


因为是公共包,下面业务的错误码均由业务自己定义,公共包只定义 String 基础类。


文章转载自:patientcat

原文链接:https://www.cnblogs.com/patientcat/p/17995605

体验地址:http://www.jnpfsoft.com/?from=001

用户头像

EquatorCoco

关注

还未添加个人签名 2023-06-19 加入

还未添加个人简介

评论

发布
暂无评论
云API错误码的设计规则_Python_EquatorCoco_InfoQ写作社区