Dubbo自定义异常全局统一处理

前言:
在业务的开发过程当中,我们往往会有各种各样的业务性异常,比如创建订单操作。
假如创建订单操作分成两步,第一步是先创建订单消息,第二步是扣除用户余额。
假如在扣除用户余额的过程中,发现该用户的余额不足,那么我们可能需要给用户反馈一个余额不足的提醒。那么就有两种方式。
第一种,返回一个自己包装的响应对象,return出去(这种如果操作涉及多个事务修改操作,那么之前的事务是可以正常提交的,会有问题,需要视情况而定)。
第二种,直接throw一个自定义的业务异常,然后全局异常统一处理成响应对象再return出去。
按上面的情况来看,如果扣余额失败,那么创建的订单我是需要让他回滚的。这样数据才不会混乱,照这样的话我们就应该直接throw自己的业务性异常,然后需要再一个地方统一包装处理再展示给用户看。

背景
以上的这个场景大家应该都明白,那么现在问题来了。一般自定义的异常(假设自定义异常为MyException)我们都会放在common或者core模块下,方便多应用统一一个异常类。但是这样就会出现一个问题。在dubbo的分布式环境下,假如我有一个工程 Project A。下面有三个模块应用,一个app服务,一个order服务,还有一个account服务。app服务可以理解为网关应用,使用Http跟前端应用进行交互,然后再使用dubbo协议跟内部的order服务进行交互创建生成订单,然后order服务在生产订单的过程中还会调用account服务来扣减账户余额。

Dubbo自定义异常全局统一处理_第1张图片

如果order服务在处理中抛出了MyException,我们在app服务想去catch住MyException是抓捕不到的,拿到的将会是RuntimeException,而不是我们想要的自定义异常(原因是因为dubbo的ExceptionFilter会对dubbo调用中发生的异常进行一系列处理,最后再包装成RuntimeException抛给服务调用方)
这样的话,我们对我们自定义的异常做统一处理时拿到的是处理过的运行时异常,不方便统一处理。

综合以上的问题,我自己是考虑到使用Dubbo的过滤器来做统一异常处理,这是我自己想的一种实现方式,大家可以参考结合自己项目看可不可以这样玩。闲话不多说,我们直接放代码。

首先接入我们的过滤器,主要分三步。 第一步,写一个实现了dubbo包下的filter的自定义filter类

//在这里首先你还得理解一个概念,dubbo当中区分一个消费者跟生产者
//在使用dubbo过滤器的时候,我们可以指定当前过滤器是在当前应用处于生产者还是处于消费者时才激活。

//这里的激活注解中的group注意一定要选择 group = {PROVIDER}
//并且这个过滤器的位置要位于order应用当中,这样的话,网关发起的请求打到order
//不论是order应用调用发生异常还是order调用的下层应用发生异常都可以在这里捕捉到。
@Activate(group = {PROVIDER})
@Slf4j
public class ExceptionFilter implements Filter {

    @Override
    public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
        Result result = invoker.invoke(invocation);
        if (result.hasException() && invoker.getInterface() != GenericService.class) {
            Throwable exception = result.getException();
            if (exception instanceof MyException) {
            	//这里看过result对象体的就会明白,他的value如果调用正常存的就是调用结果
            	//如果调用异常存的就是对应的异常内容,所以我们可以在这里对result进行重构
            	//从而将我们自己定义的异常封装成我们自己的响应对象给到前端。
                result = new AsyncRpcResult(invocation);
                result.setValue(buildExceptionResponse(exception));
                return result;
            }else if (exception instanceof Exception) {
                 //其他非自定义异常这里可以再包装,道理是一样的
                return result;
            }
        }
        return result;
    }



    /**
     * 包装自定义异常成统一响应
     * @param exception
     * @return
     */
    private ResponseVO buildExceptionResponse(Throwable exception) {
        MyExceptionexcep = (MyException)exception;
        return ResponseUtils.buildFail(excep.getErrCode(), excep.getErrMsg());
    }
}

第二步,我们需要在应用下建立一个这样的文件目录以及文件,不用选择什么后缀,直接建立就可以了
在这里插入图片描述

第三步,我们需要在第二步建立好的文件夹下再输出一段代码内容,这段代码内容会因为dubbo的SPI协议而被解析注入到应用当中,使你配置过滤器生效
配置格式如下:直接使用过滤器的类名放在等号之前,比如我们自定义的异常过滤器类名为ExceptionFilter,然后这个类对应的包路径为 com.common.exception。
那么我们在文件夹下输出的代码内容则为
exceptionFilter=com.common.exception.ExceptionFilter
这样的话,应用启动你的应用,filter就会被激活了。然后你就可以开始愉快的测试了。

你可能感兴趣的:(dubbo)