1. 引言
微服务是一种架构风格,它由一组小型服务组成,这些服务可以独立部署、独立扩展和独立升级。微服务的好处是显而易见的,但是由于微服务的复杂性,开发和运维都变得更加困难,特别是对于异常处理和错误码管理的需要更加关注。
2. 微服务异常处理
2.1 异常基础
在 Java 中,异常都是 Throwable 类的子类,可以分为检查异常(Checked Exception)和非检查异常(Unchecked Exception)两种。检查异常必须要在方法的 throws 子句中声明,而非检查异常则不需要。
/**
* 检查异常
*/
public void readFile() throws FileNotFoundException {
File file = new File("file.txt");
Scanner scanner = new Scanner(file); // 编译错误:必须声明抛出异常
}
/**
* 非检查异常
*/
public void divide() {
int a = 1 / 0; // 运行时错误:ArithmeticException
}
对于微服务来说,异常处理的基础就是对 Java 异常机制的理解。
2.2 微服务异常处理方式
微服务异常处理方式需特别关注以下三个方面:
2.2.1 统一异常处理
在微服务架构中,异常需要统一处理,方便排查问题。通常情况下,可以通过 Spring 提供的 @ControllerAdvice 注解来统一处理异常。示例如下:
@RestControllerAdvice
public class GlobalExceptionHandler {
private Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);
@ExceptionHandler(value = Exception.class)//这里你可以写多个异常进行拦截,比如:NullPointerException.class
public Object errorHandler(HttpServletRequest request, HttpServletResponse response, Exception e) {
logger.error("Error occurred during processing request at URL" + request.getRequestURL(), e);
// 这里可以返回 JSON 错误信息,也可以返回自定义错误页面
return "Error occurred, please check the log for details";
}
}
2.2.2 防止异常信息暴露
异常信息中可能包含敏感信息,比如数据库表名、字段名等,需要注意防止这些信息泄露。
可以在统一异常处理中进行拦截,过滤掉不必要的信息,示例如下:
@Slf4j
@RestControllerAdvice
public class RestControllerExceptionHandler {
/**
* 拦截 Exception 类型的异常
*/
@ExceptionHandler({ Exception.class })
public ResponseEntity handleException(Exception e) {
log.error("系统发生异常:", e);
String errMsg = "系统内部异常,请稍后重试。";
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(new ApiResponse(ResponseCode.INTERNAL_SERVER_ERROR.getCode(), errMsg));
}
/**
* 拦截 RestClientException 类型的异常
*/
@ExceptionHandler({ RestClientException.class })
public ResponseEntity handleRestClientException(RestClientException e) {
log.error("restTemplate 请求失败:", e);
String errMsg = "服务调用异常,请稍后重试。";
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(new ApiResponse(ResponseCode.REST_EXCEPTION.getCode(), errMsg));
}
}
2.2.3 自定义异常
微服务中需要自定义异常,一般继承 RuntimeException,方便方法没有 throws 声明,也不需要 try-catch 包裹。
自定义异常需注意以下两个方面:
2.2.3.1 异常码管理
在微服务中,异常码管理变得尤为重要。通过定义异常码,可以快速定位并解决错误。
可以将错误码定义为枚举类型,一般包含编号和错误信息两个字段。示例如下:
@Getter
@AllArgsConstructor
public enum ExceptionCodeEnum {
INVALID_PARAMETER("0001","参数无效"),
DATABASE_ERROR("0500","数据库连接异常"),
;
private String code;
private String message;
}
2.2.3.2 异常信息国际化
对于不同的语种,异常信息需要进行相应的国际化处理。
可以通过使用 Spring 提供的 MessageSource 类来做国际化处理。需要在配置文件中定义不同语种对应的消息。
@GetMapping("/")
public String hello(@RequestParam(value = "locale", required = false) String locale) {
Locale currentLocale = LocaleContextHolder.getLocale();
if (locale != null) {
String[] arr = locale.split("_");
if (arr.length == 2) {
currentLocale = new Locale(arr[0], arr[1]);
}
}
return messageSource.getMessage("hello.world", null, currentLocale);
}
3. 错误码管理
3.1 错误码的意义
错误码是标识错误的唯一标识,可以帮助我们更快的定位错误,同时可以帮助产品经理、开发人员更好的理解和管理错误。
3.2 Java 实现错误码管理的方法
Java 实现错误码管理的方法有很多,但是常用的主要有以下两种:
3.2.1 枚举
可以利用枚举类型来管理错误码,具体做法如下:
public enum ResultEnum {
SUCCESS(200, "成功"),
NO_HANDLER_FOUND(404, "请求路径错误"),
PARAM_ERROR(1000, "参数错误"),
SERVER_ERROR(500, "服务器错误");
private Integer code;
private String message;
ResultEnum(Integer code, String message) {
this.code = code;
this.message = message;
}
public Integer getCode() {
return code;
}
public String getMessage() {
return message;
}
}
3.2.2 properties 文件
通过使用 properties 文件来维护错误码和错误信息之间的映射,方便维护和增加多语言支持。
可以定义多个不同语言版本的 properties 文件,通过 Spring 提供的 MessageSource 类实现国际化支持。
示例如下:
# Error Code
error.param.error.code=1001
error.no.such.element.code=1002
# Error Message
error.param.error=参数错误
error.no.such.element=元素不存在
4. 结论
以上就是 Java 实现微服务异常处理和错误码管理的方法介绍。
微服务异常处理需要注意统一处理、防止信息泄露、自定义异常三个方面。错误码管理可通过枚举和 properties 文件两种方式实现。
希望本文能够帮助各位读者更好地理解和处理微服务中的异常与错误码问题。