Dubbo讲究(一)

第一次接触dubbo好像是在14年吧,13年参加工作时,在一家半国企,当时他们用的是axis2,当时那一堆代码+XML,让我怀疑是否应该继续干IT,觉得太难用了。后来接触了dubbo,觉得这玩意好,省了很多事,感概开源的力量和大公司的体量。用起来真实很通畅。

当然我们公司也没有作妖,非要选个与众不同所以还是选了dubbo作为服务之间的通信框架。

我这儿也不做什么源码解析了,比我专业的细致的网上很多,我主要解析一下我在用dubbo过程中遇到的问题相关的源码。比如今天讲的就是dubbo的异常机制、序列化协议hession相关的源码。

问题起因在于我们系统中定义了自己的一套异常处理机制,有个自定义异常

图中的ApiException是我们定义的针对接口异常的异常类,长成下图这样。

红框的部分是这次发生问题之后加的一个构造参数,加完之后就搞定了。那为什么呢?我们跟着异常信息我们知道是dubbo用的hession那块反序列化报了异常,跟下源码看下。

  • 我们找到JavaDeserializer的instantiate方法,找到_constructor,发现是个Constructor,找到赋值地方,在JavaDeserializer实例化时赋值,进入JavaDeserializer构造方法,根据赋值代码,并且因为我们的ApiException是不知一个构造函数的所以_constructor肯定不会为null,所以
    public JavaDeserializer(Class cl) {
    	this._type = cl;
        this._fieldMap = this.getFieldMap(cl);
        this._readResolve = this.getReadResolve(cl);
        if (this._readResolve != null) {
            this._readResolve.setAccessible(true);
        }

        Constructor[] constructors = cl.getDeclaredConstructors();
        long bestCost = 9223372036854775807L;

        for(int i = 0; i < constructors.length; ++i) {
            Class[] param = constructors[i].getParameterTypes();
            long cost = 0L;

            for(int j = 0; j < param.length; ++j) {
                cost = 4L * cost;
                if (Object.class.equals(param[j])) {
                    ++cost;
                } else if (String.class.equals(param[j])) {
                    cost += 2L;
                } else if (Integer.TYPE.equals(param[j])) {
                    cost += 3L;
                } else if (Long.TYPE.equals(param[j])) {
                    cost += 4L;
                } else if (param[j].isPrimitive()) {
                    cost += 5L;
                } else {
                    cost += 6L;
                }
            }

            if (cost < 0L || cost > 65536L) {
                cost = 65536L;
            }

            cost += (long)param.length << 48;
            if (cost < bestCost) {
                this._constructor = constructors[i];
                bestCost = cost;
            }
        }

        if (this._constructor != null) {
            this._constructor.setAccessible(true);
            Class[] params = this._constructor.getParameterTypes();
            this._constructorArgs = new Object[params.length];

            for(int i = 0; i < params.length; ++i) {
                this._constructorArgs[i] = getParamArg(params[i]);
            }
        }

    }

根据下面这段代码,从JavaDeserializer的构造方法中可以看出,这里_constructor会被赋予参数最少的那个构造器,即找到ApiException一个参数的构造方法 public ApiException(Throwable cause),

有根据下面这段代码,我们知道Throwable不是primitive(原始数据类型),所以_constructorArgs值为null。

**this._constructor.newInstance(this._constructorArgs)**使用反射调用构造方法时,要求给的参数必须是原始类型或者其包装器

但是我们给的参数值都为null,,传入的参数不合法,造成了上面的异常。

解决办法

所以绕了这么大一圈知道了原因,那怎么办呢,最简单的方式ApiException提供一个无参的构造方法。就会这样调用:ca.newInstance(new Object[]),即无参构造则不会再抛异常了。

小结

其实这块还会牵扯处很多东西,比如为什么ApiException要那么设计?为什么Java本身的反序列化机制能过而Hession不能过?再比如Constructor.newInstance()和我们经常看到的Class.newInstance()有什么区别?…哎 学海无涯苦作舟