什么是错误码
阿里巴巴 《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);
}
}
)