文档章节

Dubbo自定义异常message过长解决

Mr_Qi
 Mr_Qi
发布于 2017/07/04 09:36
字数 847
阅读 1096
收藏 25

参考问题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传给消费者的

© 著作权归作者所有

共有 人打赏支持
Mr_Qi

Mr_Qi

粉丝 280
博文 359
码字总数 369228
作品 0
南京
程序员
私信 提问
加载中

评论(1)

匡氏家族
匡氏家族
分析的很到位
Dubbo之telnet实现

我们可以通过telnet来访问道对应dubbo服务的信息 比如 1737165qEu871390.png 我们可以利用一些指令来访问。 我们知道,默认情况下,dubbo使用netty做transport。 那么dubbo是如何区分开正常业...

波波维奇
2017/11/30
0
0
Dubbo消费端捕获到服务提供者抛出的自定义异常,e.getMessage(),返回并不是message信息,而是整个异常的堆栈信息,请问怎么解决?

Dubbo消费端捕获到服务提供者抛出的自定义异常,e.getMessage(),返回并不是message信息,而是整个异常的堆栈信息,请问怎么解决?我需要拿到message信息给前端弹出提示,可是通过e.getMessag...

颖辉小居
2018/11/05
68
3
Dubbo处理TCP拆包粘包问题

Dubbo处理TCP拆包粘包问题 在TCP网络传输工程中,由于TCP包的缓存大小限制,每次请求数据有可能不在一个TCP包里面,或者也可能多个请求的数据在一个TCP包里面。那么如果合理的decode接受的T...

Bieber
2015/08/03
0
13
DubboShutdownHook 与 springboot 中SpringApplication registerShutdownHook 冲突

主要版本: springboot 1.3.6.RELEASE; spring-context 4.2.7.RELEASE; spring-test 4.2.7.RELEASE; dubbo: 2.8.4;Junit4.12; 错误日志: 1、输出test日志后再次执行ApplicationListener 2......

noob_fly
2017/07/28
627
0
Dubbo 2.0.9 发布,阿里巴巴开源服务框架

阿里巴巴开源服务框架Dubbo2.0.9版本发布了,该版本增加了简易监控中心界面,以及修复了一些BUG。 Dubbo首页:http://code.alibabatech.com/wiki/display/dubbo/Home 下载地址:http://code...

红薯
2011/12/14
15.3K
6

没有更多内容

加载失败,请刷新页面

加载更多

看过上百部片子的这个人教你视频标签算法解析

本文由云+社区发表 随着内容时代的来临,多媒体信息,特别是视频信息的分析和理解需求,如图像分类、图像打标签、视频处理等等,变得越发迫切。目前图像分类已经发展了多年,在一定条件下已经...

腾讯云加社区
15分钟前
0
0
2. 红黑树

定义:红黑树(Red-Black Tree,简称R-B Tree),它一种特殊的二叉查找树(Binary Search Tree)。 要理解红黑树,先要了解什么是二叉查找树。在上一章中,我们学习了什么是二叉树,以及二叉树...

火拳-艾斯
16分钟前
0
0
input的button类型,点击页面跳转

一、input type=button 不做任何操作 例如: <input type="button" class="btn btn-primary" style="width: 30%" value="返回" onclick="window.location.href='/users/list'"></input> onc......

Sunki
22分钟前
0
0
踩坑:js 小数运算出现精度问题

背景 在学习小程序商城源码时发现了这个问题,单价可能出现小数,小数之间运算结果会莫名其妙多出一大串数字,比如下面这样👇。 在此之前我是知道 js 中著名的 0.1 + 0.2 != 0.3 的问题的,...

dkvirus
27分钟前
0
0
zookeeper和HBASE总结

zookeeper快速上手 zookeeper的基本功能和应用场景 zookeeper的整体运行机制 zookeeper的数据存储机制 数据存储形式 zookeeper中对用户的数据采用kv形式存储 只是zk有点特别: key:是以路径...

瑞查德-Jack
50分钟前
1
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部