错误码设计

370 阅读3分钟

什么是错误码

阿里巴巴 《Java 开发手册》 - 异常日志 - 错误码

错误码的制定原则: 快速溯源、简单易记、沟通标准化。

错误码的要求

1、错误码必须能够快速知晓错误来源,可快速判断是谁的问题。

2、错误码易于记忆和对比。

3、错误码能够脱离文档和系统平台达到线下轻量化地自由沟通的目的。

异常 vs 错误码

1、异常类因为复用性不能很快的进行定位,异常类的代码行数不是一个稳定的值 2、异常类不具有可比性,且不利于前后端交互 3、异常类只能存在与 Java 代码中。

错误码设计

错误码设计只需要定义一个数字和一个描述信息。但是需要很好的标识出具体的 项目 + 模块 + 业务 等相关信息。

错误码的分层

大部分项目的错误码设计分为 3 级能满足业务场景,项目、模块、错误编码。错误码 5 位 项目编码 (2 位) + 模块编码 (2 位) + 错误编码 (1 位)

错误码表达方式

推荐使用枚举,因为枚举具有不可变性,且所有值都在一个文件里描述。

多模块错误码定义及接口定义

原始的错误定义方法是项目中所有的错误码都定义在一个类,但是这样会随着业务的发展错误码越来越多,最终难以维护:

1、项目编码、模块编码的维护:另建一个枚举类统一维护。 2、异常类的统一引用: 定义接口,枚举类实现接口。

例子:


//异常接口定义
public interface ErrorCode {
}
//模块定义
public enum UserProjectCodes {
    LOGIN(1, 1, "登录模块"),
    USER(1, 2, "用户模块")
}
//登录模块异常码定义
public enum LoginErrorCodes implements ErrorCode {
    USER_NOT_EXIST(0, "用户名不存在"), //错误码: 10100
    PASSWORD_ERROR(1, "密码错误");    //错误码: 10101
    
    private final int nodeNum;
    private final String msg;

    UserLoginErrorCodes(int nodeNum, String msg) {
        this.nodeNum = nodeNum;
        this.msg = msg;
        ErrorManager.register(UserProjectCodes.LOGIN, this);
    }
}

防重设计

错误码本质是一个数字,且每一个都需要由 RD 编码定义,在错误码多的项目中很容易重复。最佳实践是在枚举的构造方法里调用 Helper 类,Helper 来统一维护所有的异常码,如有重复则枚举初始化失败。

例子:


//错误码定义
PARAM_ERROR(17, "参数非法,期望得到:{},实际得到:{}")
//错误码使用
ErrorCodes.PARAM_ERROR.format(arg1, arg2);

实现方式:

org.slf4j.helpers.MessageFormatter.arrayFormat(this.message, args).getMessage()  

异常与错误码的使用场景

经验:线程内用异常 | RPC 调用错误码

错误码

优势:

无额外性能损耗 包含信息丰富

劣势:

写起来麻烦 无法用于异步场景

异常

优势:

能够直接中断逻辑 写起来方便

劣势:

性能较差 信息单一 无法用于异步场景

异步下的错误处理

异步场景下错误处理的关键就是两个字: 回调

例子:

public interface CallBack<T> {
    
    void handle(T result, Throwable e);

}

public class TestService {


    // 同步方法
    public Response call() {
        return doBiz();
    }
    
    // 异步方法
    public void callAsync(CallBack<Response> callBack) {
        new Thread(() -> {
        
             Response response = null;
             Throwable throwable = null;
             try {
                 response = doBiz();
             } catch (Exception e) {
                 throwable = e;
             }
            callBack.handle(response,throwable); 
        }).start();
    
    }

}



callAsync((response, throwable) -> {
    
    if (throwable != null) {
        log.error("exception error", e);
    }

    }
)