我们在自己的业务系统中,通常会用到自定义的业务异常类,这个异常会继承extends RuntimeException,当发生业务限制的时候,会throw出来。但是在使用dubbo进行soa治理的时候,会发现provider抛出的异常,在custom端并不能正确的捕获。即便我们在provider和custom都有导入相同framework.jar下面的BusinessException异常,并且抛出这个异常,在Consumer端只会打印一行异常信息,获取不到正常的异常。百度才知道Dubbo对抛出得异常用一个ExceptionFilter的类进行拦截。
在一个公共包里定义一个异常类,继承自RuntimeException。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 public class BusinessException extends RuntimeException { private String code; public BusinessException () { super (); } public BusinessException (String message) { super (message); } public BusinessException (String code, String message) { super (message); this .code = code; } public BusinessException (String message, Throwable cause) { super (message, cause); } public BusinessException (String code, String message, Throwable cause) { super (message, cause); this .code = code; } public BusinessException (Throwable cause) { super (cause); } public String getCode () { return code; } public void setCode (String code) { this .code = code; } }
在服务提供者的Dubbo配置文件中添加下面的内容,主要就是自定义exceptionFilter,然后排出dubbo自带的exceptionFilter
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 <?xml version="1.0" encoding="UTF-8"?> <beans xmlns ="http://www.springframework.org/schema/beans" xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xmlns:dubbo ="http://code.alibabatech.com/schema/dubbo" xsi:schemaLocation ="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd" > <dubbo:application name ="dubbo-web-provide" /> <dubbo:registry address ="zookeeper://localhost:2181" timeout ="30000" > <dubbo:parameter key ="max.idle" value ="10" /> <dubbo:parameter key ="min.idle" value ="5" /> <dubbo:parameter key ="max.active" value ="20" /> <dubbo:parameter key ="max.total" value ="100" /> </dubbo:registry > <dubbo:provider filter ="dubboExceptionFilter,-exception" payload ="123886080" > </dubbo:provider > <dubbo:protocol name ="dubbo" port ="20880" accesslog ="true" serialization ="hessian2" /> <bean id ="helloService" class ="cn.ydstudio.dubbo.web.provide.serviceimpl.HelloServiceImpl" /> <bean id ="goodsService" class ="cn.ydstudio.dubbo.web.provide.serviceimpl.GoodsServiceImpl" /> <bean id ="goodsService2" class ="cn.ydstudio.dubbo.web.provide.serviceimpl.GoodsServiceImpl2" /> <bean id ="goodsOrderServiceRemoteImpl" class ="cn.ydstudio.dubbo.web.provide.serviceimpl.GoodsOrderServiceRemoteImpl" /> <dubbo:service interface ="cn.ydstudio.dubbo.rpc.service.HelloService" ref ="helloService" version ="1.0.0" retries ="1" /> <dubbo:service interface ="cn.ydstudio.dubbo.rpc.service.GoodsService" ref ="goodsService" version ="1.0.0" retries ="1" /> <dubbo:service interface ="cn.ydstudio.dubbo.rpc.service.GoodsService" ref ="goodsService2" version ="2.0.0" retries ="1" /> <dubbo:service interface ="cn.ydstudio.dubbo.rpc.service.GoodsOrderServiceRemote" ref ="goodsOrderServiceRemoteImpl" version ="1.0.0" retries ="0" > <dubbo:method name ="querGoodsOrderList" timeout ="1000000" /> </dubbo:service > </beans >
最重要的继承ExceptionFilter,然后重写invoke方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 package cn.ydstudio.dubbo.web.provide.filter;import cn.ydstudio.common.tools.exception.BizException;import cn.ydstudio.common.tools.exception.BusinessException;import com.alibaba.dubbo.common.logger.Logger;import com.alibaba.dubbo.common.logger.LoggerFactory;import com.alibaba.dubbo.common.utils.ReflectUtils;import com.alibaba.dubbo.common.utils.StringUtils;import com.alibaba.dubbo.rpc.*;import com.alibaba.dubbo.rpc.filter.ExceptionFilter;import com.alibaba.dubbo.rpc.service.GenericService;import java.lang.reflect.Method;public class DubboExceptionFilter extends ExceptionFilter { private final Logger logger; public DubboExceptionFilter () { this (LoggerFactory.getLogger(com.alibaba.dubbo.rpc.filter.ExceptionFilter.class )) ; } public DubboExceptionFilter (Logger logger) { this .logger = logger; } @Override public Result invoke (Invoker<?> invoker, Invocation invocation) throws RpcException { try { Result result = invoker.invoke(invocation); if (result.hasException() && GenericService.class ! = invoker.getInterface()) { try { Throwable exception = result.getException(); if (exception instanceof BizException || exception instanceof BusinessException){ return result; } if (!(exception instanceof RuntimeException) && exception instanceof Exception) { return result; } else { try { Method method = invoker.getInterface().getMethod(invocation.getMethodName(), invocation.getParameterTypes()); Class<?>[] exceptionClassses = method.getExceptionTypes(); Class[] arr$ = exceptionClassses; int len$ = exceptionClassses.length; for (int i$ = 0 ; i$ < len$; ++i$) { Class<?> exceptionClass = arr$[i$]; if (exception.getClass().equals(exceptionClass)) { return result; } } } catch (NoSuchMethodException var11) { return result; } this .logger.error("Got unchecked and undeclared exception which called by " + RpcContext.getContext().getRemoteHost() + ". service: " + invoker.getInterface().getName() + ", method: " + invocation.getMethodName() + ", exception: " + exception.getClass().getName() + ": " + exception.getMessage(), exception); String serviceFile = ReflectUtils.getCodeBase(invoker.getInterface()); String exceptionFile = ReflectUtils.getCodeBase(exception.getClass()); if (serviceFile != null && exceptionFile != null && !serviceFile.equals(exceptionFile)) { String className = exception.getClass().getName(); if (!className.startsWith("java." ) && !className.startsWith("javax." )) { return (Result) (exception instanceof RpcException ? result : new RpcResult(new RuntimeException(StringUtils.toString(exception)))); } else { return result; } } else { return result; } } } catch (Throwable var12) { this .logger.warn("Fail to ExceptionFilter when called by " + RpcContext.getContext().getRemoteHost() + ". service: " + invoker.getInterface().getName() + ", method: " + invocation.getMethodName() + ", exception: " + var12.getClass().getName() + ": " + var12.getMessage(), var12); return result; } } else { return result; } } catch (RuntimeException var13) { this .logger.error("Got unchecked and undeclared exception which called by " + RpcContext.getContext().getRemoteHost() + ". service: " + invoker.getInterface().getName() + ", method: " + invocation.getMethodName() + ", exception: " + var13.getClass().getName() + ": " + var13.getMessage(), var13); throw var13; } } }
然后在provider可以直接抛出异常,在consumer可以直接捕捉到。consumer服务消费端可以新建一个全局异常处理的控制器,可以格式化后输出给前端。