Dubbo自定义异常message过长解决


声明:本文转载自https://my.oschina.net/qixiaobo025/blog/1153876,转载目的在于传递更多信息,仅供学习交流之用。如有侵权行为,请联系我,我会及时删除。

参考问题Dubbo异常处理

由于dubbo会将自定义异常或者第三方异常包装直接放入RuntimeException,并且使用了 

StringUtils.toString(exception))

按照dubbo的服务化最佳实践

异常

  • 建议使用异常汇报错误,而不是返回错误码,异常信息能携带更多信息,以及语义更友好,
  • 如果担心性能问题,在必要时,可以通过override掉异常类的fillInStackTrace()方法为空方法,使其不拷贝栈信息,
  • 查询方法不建议抛出checked异常,否则调用方在查询时将过多的try...catch,并且不能进行有效处理,
  • 服务提供方不应将DAO或SQL等异常抛给消费方,应在服务实现中对消费方不关心的异常进行包装,否则可能出现消费方无法反序列化相应异常。

 

基于现状目前引用的第三方异常以及自定义异常很难一次性加入到api包中。并且unchecked异常也很难全部声明到方法签名上。

遂做了如下改造

/**  * Created by qixiaobo on 2017/7/3.  */ @Activate(group = Constants.PROVIDER, before = {"exception"}, value = {"customException"}) public class CustomExceptionFilter implements Filter {     private static Logger logger = LoggerFactory.getLogger(CustomExceptionFilter.class);       @Override     public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {         Result result = invoker.invoke(invocation);         if (result.hasException() && GenericService.class != invoker.getInterface()) {             try {                 Throwable exception = result.getException();                 // 如果是checked异常,直接抛出                 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;                 }                 // 是JDK自带的异常,直接抛出                 String className = exception.getClass().getName();                 if (className.startsWith("java.") || className.startsWith("javax.")) {                     return result;                 }                 // 是Dubbo本身的异常,直接抛出                 if (exception instanceof RpcException) {                     return result;                 }                 //其他exception ,减少问题,直接将exception序列化成RuntimeException,同时放入指定的异常类型值attachment中                 // 否则,包装成RuntimeException抛给客户端                 RpcResult rpcResult = new RpcResult(new RuntimeException(exception.getMessage()));                 rpcResult.setAttachment("customException", exception.getClass().getName());//已经包装过后续ExceptionFilter无需处理                 result = rpcResult;             } catch (Throwable e) {                 logger.error(e.getMessage(), e);                 return result;             }         }         return result;     } }
customException=com.air.tqb.dubbo.filter.CustomExceptionFilter
<dubbo:provider timeout="30000" group="${dubbo.group}" retries="0" owner="qixiaobo" id="f6-provider" filter="customException"/> 

效果自然是有的

策略比较简单:

  1. 在ExceptionFilter处理之前提前将异常转化成RuntimeException,将原异常的message取出,放入RuntimeException
  2. 放入Attachment到RpcResult以期许在消费者获取具体的异常类型。

结果却是可以拿到较短的message,并且消除了大量的堆栈填充。但是在消费者却无法获取指定的attachment。

直接给出结论DubboCodec中:

@Override protected void encodeResponseData(Channel channel, ObjectOutput out, Object data) throws IOException {     Result result = (Result) data;       Throwable th = result.getException();     if (th == null) {         Object ret = result.getValue();         if (ret == null) {             out.writeByte(RESPONSE_NULL_VALUE);         } else {             out.writeByte(RESPONSE_VALUE);             out.writeObject(ret);         }     } else {         out.writeByte(RESPONSE_WITH_EXCEPTION);         out.writeObject(th);     } }

很明显在做encodeResponse的时候Dubbo直接取出了对应的类型

  1. 如果异常为空取出value
  2. 如果value为空则写入空返回(异步或者oneWay)
  3. 如果value不为空则写入value
  4. 否则将异常写入

很明显 期间并未操作到attachment。

那么在请求的时候我们查看一下

@Override protected void encodeRequestData(Channel channel, ObjectOutput out, Object data) throws IOException {     RpcInvocation inv = (RpcInvocation) data;       out.writeUTF(inv.getAttachment(Constants.DUBBO_VERSION_KEY, DUBBO_VERSION));     out.writeUTF(inv.getAttachment(Constants.PATH_KEY));     out.writeUTF(inv.getAttachment(Constants.VERSION_KEY));       out.writeUTF(inv.getMethodName());     out.writeUTF(ReflectUtils.getDesc(inv.getParameterTypes()));     Object[] args = inv.getArguments();     if (args != null)     for (int i = 0; i < args.length; i++){         out.writeObject(encodeInvocationArgument(channel, inv, i));     }     out.writeObject(inv.getAttachments()); } 

确实从invocation中取出了attachment并写入请求流中。

因此在消费者通过DubboCodec是无法将attachment传给消费者的

本文发表于2017年07月04日 10:36
(c)注:本文转载自https://my.oschina.net/qixiaobo025/blog/1153876,转载目的在于传递更多信息,并不代表本网赞同其观点和对其真实性负责。如有侵权行为,请联系我们,我们会及时删除.

阅读 2369 讨论 0 喜欢 1

抢先体验

扫码体验
趣味小程序
文字表情生成器

闪念胶囊

你要过得好哇,这样我才能恨你啊,你要是过得不好,我都不知道该恨你还是拥抱你啊。

直抵黄龙府,与诸君痛饮尔。

那时陪伴我的人啊,你们如今在何方。

不出意外的话,我们再也不会见了,祝你前程似锦。

这世界真好,吃野东西也要留出这条命来看看

快捷链接
网站地图
提交友链
Copyright © 2016 - 2021 Cion.
All Rights Reserved.
京ICP备2021004668号-1