dubbo建议请求异常返回堆栈而不是错误码(异常也是dubbo判断调用是否成功的一个因素,如果返回错误码集群的失败重试就会失效),下面我们分别从provider与consumer进行分析。
provider
我们知道dubbo将服务包装为invoker,下面为AbstractInvoker源码
public Result invoke(Invocation inv) throws RpcException {
省略...
try {
//实际执行业务方法
return doInvoke(invocation);
} catch (InvocationTargetException e) { // 反射调用发生异常,将异常封装到RpcResult
Throwable te = e.getTargetException();
if (te == null) {
return new RpcResult(e);
} else {
if (te instanceof RpcException) {
((RpcException) te).setCode(RpcException.BIZ_EXCEPTION);
}
return new RpcResult(te);
}
} catch (RpcException e) {
if (e.isBiz()) {
return new RpcResult(e);
} else {
throw e;
}
} catch (Throwable e) {
return new RpcResult(e);
}
}
异常过滤器 ExceptionFilter
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 RuntimeException) && (exception instanceof Exception)) {
return result;
}
// 如果接口已经声明了抛出该异常直接返回结果
try {
Method method = invoker.getInterface().getMethod(invocation.getMethodName(), invocation.getParameterTypes());
Class<?>[] exceptionClassses = method.getExceptionTypes();
for (Class<?> exceptionClass : exceptionClassses) {
if (exception.getClass().equals(exceptionClass)) {
return result;
}
}
} catch (NoSuchMethodException e) {
return result;
}
// 如果是未声明的运行时异常在日志中输出异常(换句话说,除了这种情况,provider是不会打印异常的)
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);
// directly throw if exception class and interface class are in the same jar file.
String serviceFile = ReflectUtils.getCodeBase(invoker.getInterface());
String exceptionFile = ReflectUtils.getCodeBase(exception.getClass());
if (serviceFile == null || exceptionFile == null || serviceFile.equals(exceptionFile)) {
return result;
}
// directly throw if it's JDK exception
String className = exception.getClass().getName();
if (className.startsWith("java.") || className.startsWith("javax.")) {
return result;
}
// directly throw if it's dubbo exception
if (exception instanceof RpcException) {
return result;
}
// otherwise, wrap with RuntimeException and throw back to the client
return new RpcResult(new RuntimeException(StringUtils.toString(exception)));
} catch (Throwable e) {
logger.warn("Fail to ExceptionFilter when called by " + RpcContext.getContext().getRemoteHost()
+ ". service: " + invoker.getInterface().getName() + ", method: " + invocation.getMethodName()
+ ", exception: " + e.getClass().getName() + ": " + e.getMessage(), e);
return result;
}
}
return result;
} catch (RuntimeException e) {
logger.error("Got unchecked and undeclared exception which called by " + RpcContext.getContext().getRemoteHost()
+ ". service: " + invoker.getInterface().getName() + ", method: " + invocation.getMethodName()
+ ", exception: " + e.getClass().getName() + ": " + e.getMessage(), e);
throw e;
}
}
Consumer
InvokerInvocationHandler 调用 invoker.invoke(invocation).recreate();
public class RpcResult implements Result, Serializable {
public Object recreate() throws Throwable {
if (exception != null) {
throw exception;
}
return result;
}
}
由上图调用链可以看出只有集群调用失败,才是最终失败,才会把provider传来的异常抛出。(如果集群配置为失败重试,那么只有最后一次失败才会走到这里来抛出异常)