2021-08-26Dubbo踩坑笔记

背景

由于项目是多人协作,服务调用使用的dubbo,在定义接口出入参的时候,我自己定义了一个基本入参xxxReq,包含了两个分页属性,后续有其他同事将此类继承了一个BaseReq。base中包含分页字段。
本地单元测试,接口都能根据分页数据的值查询出对应的数据,但是dubbo Consumer端查出的数据一直是走的默认数据。而且服务器上,根据分页条目的属性名查询日志,根本查不到日志输出,由于默认数据与数据库条目还是一致的,所以错误的以为触发了dubbo的缓存机制,并没有真正的将请求打到服务器上,也导致排查错误浪费了很多时间。

结果

后来停掉所有服务请求,仅保留出问题的请求,发送请求后,发现日志有打印输出,只不过分页字段的值都是null。所以将目光转向了出入参,发现了父子类的字段重复了,猜测是序列化问题,去除子类中的分页属性后,重新请求,Consumer正常,问题解决。

深入学习与分析

采用的是dubbo缺省协议,序列化方式是Hessian序列化,在反序列化时会出现父类的同名属性覆盖子类的同名属性问题,所以就变成null,解析不到了。
查看dubbo源码hessian下面,有很多Serializer。
在这里插入图片描述
以JavaSerializer为例学习一下序列化过程:

public JavaSerializer(Class cl, ClassLoader loader) {
        this.introspectWriteReplace(cl, loader);
        if (this._writeReplace != null) {
            this._writeReplace.setAccessible(true);
        }

        ArrayList primitiveFields = new ArrayList();

        ArrayList compoundFields;
        int i;
        for(compoundFields = new ArrayList(); cl != null; cl = cl.getSuperclass()) {
            Field[] fields = cl.getDeclaredFields();

            for(i = 0; i < fields.length; ++i) {
                Field field = fields[i];
                if (!Modifier.isTransient(field.getModifiers()) && !Modifier.isStatic(field.getModifiers())) {
                    field.setAccessible(true);
                    if (!field.getType().isPrimitive() && (!field.getType().getName().startsWith("java.lang.") || field.getType().equals(Object.class))) {
                        compoundFields.add(field);
                    } else {
                        primitiveFields.add(field);
                    }
                }
            }
        }

        ArrayList fields = new ArrayList();
        fields.addAll(primitiveFields);
        fields.addAll(compoundFields);
        this._fields = new Field[fields.size()];
        fields.toArray(this._fields);
        this._fieldSerializers = new JavaSerializer.FieldSerializer[this._fields.length];

        for(i = 0; i < this._fields.length; ++i) {
            this._fieldSerializers[i] = getFieldSerializer(this._fields[i].getType());
        }

    }

没怎么看懂,但是for循环的大致意思就是先遍历子类的属性进行存储,然后再进行父类的属性。

以JavaDeserializer为例学习一下序列化过程:

    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]);
            }
        }

    }

后面一大堆很难懂,就看到一行this._fieldMap = this.getFieldMap(cl);这个是获取属性集合的一个方法,跟进去看一下:

protected HashMap getFieldMap(Class cl) {
        HashMap fieldMap;
        for(fieldMap = new HashMap(); cl != null; cl = cl.getSuperclass()) {
            Field[] fields = cl.getDeclaredFields();

            for(int i = 0; i < fields.length; ++i) {
                Field field = fields[i];
                if (!Modifier.isTransient(field.getModifiers()) && !Modifier.isStatic(field.getModifiers()) && fieldMap.get(field.getName()) == null) {
                    try {
                        field.setAccessible(true);
                    } catch (Throwable var8) {
                        var8.printStackTrace();
                    }

                    Class type = field.getType();
                    Object deser;
                    if (String.class.equals(type)) {
                        deser = new JavaDeserializer.StringFieldDeserializer(field);
                    } else if (Byte.TYPE.equals(type)) {
                        deser = new JavaDeserializer.ByteFieldDeserializer(field);
                    } else if (Short.TYPE.equals(type)) {
                        deser = new JavaDeserializer.ShortFieldDeserializer(field);
                    } else if (Integer.TYPE.equals(type)) {
                        deser = new JavaDeserializer.IntFieldDeserializer(field);
                    } else if (Long.TYPE.equals(type)) {
                        deser = new JavaDeserializer.LongFieldDeserializer(field);
                    } else if (Float.TYPE.equals(type)) {
                        deser = new JavaDeserializer.FloatFieldDeserializer(field);
                    } else if (Double.TYPE.equals(type)) {
                        deser = new JavaDeserializer.DoubleFieldDeserializer(field);
                    } else if (Boolean.TYPE.equals(type)) {
                        deser = new JavaDeserializer.BooleanFieldDeserializer(field);
                    } else if (Date.class.equals(type)) {
                        deser = new JavaDeserializer.SqlDateFieldDeserializer(field);
                    } else if (Timestamp.class.equals(type)) {
                        deser = new JavaDeserializer.SqlTimestampFieldDeserializer(field);
                    } else if (Time.class.equals(type)) {
                        deser = new JavaDeserializer.SqlTimeFieldDeserializer(field);
                    } else {
                        deser = new JavaDeserializer.ObjectFieldDeserializer(field);
                    }

                    fieldMap.put(field.getName(), deser);
                }
            }
        }

        return fieldMap;
    }

把序列化中的属性List放到HashMap中,HashMap: fieldMap.put(field.getName(), deser);由于map的key是field.getName(),由于父类后遍历,所以在最后把父类属性的null,传给了fieldMap。因此在反序列化时,将map读出来时出现了null值。

根据情况分析,仅代表个人愚见,有偏颇之处还望指出~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值