dubbo的Exception堆栈 丢失 处理


研究背景

希望dubbo服务端抛出的异常完整的传递给客户端,最终到达最高层,统一进行异常的处理和堆栈的记录。

不使用ExceptionFilter

会出现java原生的异常堆栈消失,没有空参构造器的自定义异常堆栈能够在服务端出现,而有空参构造器的自定义异常堆栈也消失的情况。

问题分析

public Serializer getDefaultSerializer(Class type) {
    if (type == null) {
        throw new IllegalArgumentException("type cannot be null.");
    }
    if (!type.isArray() && !ReflectionUtils.checkZeroArgConstructor(type)) {
        if (logger.isWarnEnabled()) {
            logger.warn(type + " has no zero-arg constructor and this will affect the serialization performance");
        }
        return new JavaSerializer();
    }
    return super.getDefaultSerializer(type);
}

发现,原来是dubbox优化之后造成的结果,当需要被序列化的对象没有空参构造器时,dubbox会放弃使用Kryo而使用java序列化方案。很可能就是这个优化方案导致上述怪异情况的发生。

默认的ExceptionFilter

关键代码:

2016-06-07 14:19:26.096 ERROR 14278 --- [nio-8081-exec-2] o.a.c.c.C.[.[.[/].[dispatcherServlet]    : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is java.lang.ArrayIndexOutOfBoundsException: 异常] with root cause

java.lang.ArrayIndexOutOfBoundsException: 异常

而对于最后不符合条件的异常,会使用dubbotoString()方法将异常转化为String,然后再包一层RuntimeException往外抛出,那么理论上我们将会看到一个detailMessage类似堆栈,但没有堆栈的一段信息。如下所示:

    // 否则,包装成RuntimeException抛给客户端
    return new RpcResult(new RuntimeException(StringUtils.toString(exception)));

这个乍看只是toString(),可谁知道他在里面干了什么。我们就来看一下这个toString()方法:

try {
  throw new ArrayIndexOutOfBoundsException("异常");
} catch (ArrayIndexOutOfBoundsException e) {
  e.printStackTrace()
  throw e
}

这个时候,客户端就能够完整的记录下异常的堆栈。联想到dubbotoString()方法里面也有printStackTrace()这个方法,所以他也打出了堆栈。难道printStackTrace()方法不为人知的还做了一些除了打印之外的事情?

来看下printStackTrace()最终调用的代码:

StackTraceElement[] trace = getOurStackTrace();

通过私有方法getOurStackTrace()获取了异常的堆栈:

private void fillInStackTrace(Throwable exception) {
  exception.setStackTrace(exception.getStackTrace());
}

稍微修改一下原来的invoke()逻辑,在获取到exception之后,预处理一下。

解决和不足

启用自定义的ExceptionFilter,发现问题完美解决。唯一不足之处是,由于我们对于堆栈的处理并没有处理成String,那么在异常传回客户端反序列化时,如果堆栈中包含的某些类在客户端中不存在时,会出现反序列化失败的情况,所以一定要保证所有的异常(主要是自定义异常)被客户端也依赖了。



作者:钱嘉鑫
链接:https://www.jianshu.com/p/dc9a180cca61
来源:简书
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。