一、为什么需要网关层面的全局异常处理
下图标识是一次请求,经由网关转发微服务并由微服务操作数据库的一次请求处理流程,该流程处理过程中包含5处可能出现异常的位置
- 请求到达网关,网关处理请求发生异常
- 网关进行请求转发到微服务,转发过程中服务发现异常或网络异常
- 微服务处理请求,请求处理过程发生异常
- 微服务调用操作数据库,数据库操作异常
- 数据库本身发生网络或其它异常
对于3、4、5处的异常,微服务通过ControllerAdvice + ExceptionHandler进行全局处理,返回全局通用的请求响应数据结构
对于1、2处的异常如果不进行统一处理对用户很不友好,所以也需要在网关层面进行全局异常处理,这样对于网关本身出现的异常和请求转发过程的异常也能为用户提供比较友好的响应结果。
二、自定义全局异常处理
我们可以通过实现ErrorWebExceptionHandler接口实现handle方法就可以实现全局异常处理
/**
* description:gateway异常处理器
*
* @author mawkun
* @date 2023/5/23
*/
@Slf4j
@Order(-1)
@Component
public class GatewayExceptionHandler implements ErrorWebExceptionHandler {
@Override
public Mono<Void> handle(ServerWebExchange exchange, Throwable throwable) {
ServerHttpResponse response = exchange.getResponse();
if (response.isCommitted()) {
// 对于已经committed(提交)的response,就不能再使用这个response向缓冲区写任何东西
return Mono.error(throwable);
}
CommonResult result;
response.getHeaders().setContentType(MediaType.APPLICATION_JSON);
// 按照异常类型进行转译处理
if (throwable instanceof BizException) {
result = CommonResult.fail((BizException) throwable);
} else {
result = CommonResult.fail(ResultCode.SYSTEM_ERROR);
}
writeLog(exchange, throwable);
return response.writeWith(Mono.fromSupplier(() -> {
DataBufferFactory bufferFactory = response.bufferFactory();
return bufferFactory.wrap(JSONUtil.toJsonStr(result).getBytes());
}));
}
/**
* 记录异常日志
* @param exchange
* @param throwable
*/
private void writeLog(ServerWebExchange exchange, Throwable throwable) {
ServerHttpRequest request = exchange.getRequest();
URI uri = request.getURI();
String host = uri.getHost();
int port = uri.getPort();
log.error("[gateway]-host:{} ,port:{},url:{}, errormessage:", host, port, request.getPath(), throwable);
}
}