Dubbo踩坑实录——Map序列化

4,867 阅读2分钟

发现问题

之前在做项目的时候,项目元数据查询的子项目耦合在整个大项目中,于是将元数据查询单独抽离成一个独立的项目模块,以dubbo服务的方式提供给其他项目。 但是在一次接口调用的时候,发现生产者一方返回的Map数据在消费者一方接受的时候出状况了,原本预期的返回的是{11:"zy"}这样的格式,结果debug发现消费者一方拿到的数据是{"keys":[11],"values":["zy"]} 生产者代码如下:

public Map<Integer, DigitalAlbumCache> getDigitalAlbumCacheMap(List<Integer> ids) {
        List<DigitalAlbumCache> digitalAlbumCaches = musicCacheQueryService.getDigitalAlbumCacheByIds(ids);
        if (CollectionUtils.isEmpty(digitalAlbumCaches)) {
            return null;
        }
        
        Set<DigitalAlbumCache> digitalAlbumSet = new HashSet<>(digitalAlbumCaches);
        return Maps.uniqueIndex(digitalAlbumSet.iterator(), DigitalAlbumCache::getId);

    }

定位问题

确定的一点是,在之前这一段代码在同一个项目中调用的时候是没有问题的,而RPC项目比同项目调用多的步骤就是序列化->网络传输->反序列化,所以问题大概就是出在序列化上 打开Dubbo框架源码,找到序列化的地方

可见dubbo的序列化提供了SPI接口,默认使用的是hessian2。 Google一下可以知道,hession2框架对于List, Map, Number, Date, Calendar这几类对象,只支持JDK原生的实现类的序列化与反序列化,具体可参考com.alibaba.com.caucho.hessian.io.MapSerializer#writeObject方法的实现,而我们这边生产者的返回类型是guava的ImmutableMap。 遂放弃guava,重写了这段代码,使得返回类型为HashMap,问题迎刃而解。 问题到这里就解决了,以下是hession2框架对于Map序列化反序列化的源码剖析。

---------------------------------分割线----------------------------------

源码导读

进入实现类Hessian2Serialization

可见dubbo对于序列化的基本思路是这样的:
基于SPI,提供给用户各种Serialization的实现,具体实现类中,通过调用#serialize和#deserialize方法分别返回ObjectOutput和ObjectInput的具体实现,这连个类的实现有点类似于JDK中java.io.ObjectInput和java.io.ObjectOutput的作用,是用来具体针对各种不同类型的数据结构进行序列化和反序列化的。