Dubbo源码解析-序列化的实现

前言:

前文中介绍了有关Dubbo协议的相关知识。Dubbo协议是Dubbo框架自定义的一种协议,类似于HTTP协议,也是一种应用层协议。

从Dubbo框架结构来看(参考:https://dubbo.apache.org/zh/docsv2.7/dev/design/),其位于如下位置:

 

服务消费者和提供者依据协议的模式来进行消息的发送和接收。

在消息发送的过程中,需要对消息体进行序列化操作,而Dubbo本身提供了多种序列化方式,本文就来简单了解下这些序列化方式。

1.获取序列化方式的入口

之前DubboCodec中我们有简单说明过,入口在以下位置:

public class ExchangeCodec extends TelnetCodec {
 
    // 发送数据之前进行编码操作
    public void encode(Channel channel, ChannelBuffer buffer, Object msg) throws IOException {
        if (msg instanceof Request) {
            // 关注点在这里
            encodeRequest(channel, buffer, (Request) msg);
        } else if (msg instanceof Response) {
            encodeResponse(channel, buffer, (Response) msg);
        } else {
            super.encode(channel, buffer, msg);
        }
    }
    
    protected void encodeRequest(Channel channel, ChannelBuffer buffer, Request req) throws IOException {
        // 在这里获取序列化方式
        Serialization serialization = getSerialization(channel);
        ...
    }
    
    protected Serialization getSerialization(Channel channel) {
        // 具体见1.1
        return CodecSupport.getSerialization(channel.getUrl());
    }
}

1.1 CodecSupport.getSerialization() 

public class CodecSupport {
    public static Serialization getSerialization(URL url) {
        return ExtensionLoader.getExtensionLoader(Serialization.class).getExtension(
                url.getParameter(Constants.SERIALIZATION_KEY, Constants.DEFAULT_REMOTING_SERIALIZATION));
    }
}

同样是通过SPI的方式来获取序列化方式。如果在url中没有配置的话,则默认是hessian2方式。

2.Dubbo的序列化方式

通过Serialization接口的实现类,我们可以看到Dubbo支持的序列化方式如下:

 

以上是常用的几种序列化方式,至于这些序列化方式的使用场景,在 成熟度 | Apache Dubbo  中有说明

Feature Maturity Strength Problem Advise User
Hessian Serialization Stable 性能较好,多语言支持(推荐使用) Hessian的各版本兼容性不好,可能和应用使用的Hessian冲突,Dubbo内嵌了hessian3.2.1的源码 可用于生产环境 Alibaba
Dubbo Serialization Tested 通过不传送POJO的类元信息,在大量POJO传输时,性能较好 当参数对象增加字段时,需外部文件声明 试用
Json Serialization Tested 纯文本,可跨语言解析,缺省采用FastJson解析 性能较差 试用
Java Serialization Stable Java原生支持 性能较差 可用于生产环境

而如果是纯java应用,则可以考虑使用性能更高的Kryo/FST序列化方式,在官网有关于其性能测试结果,具体在 Kryo 和 FST 序列化 | Apache Dubbo 

最终结果如下两张图:

 

 

由以上测试结果可以看到:Kryo/FST相对其他序列化方式有显著的改进。

3.序列化源码解析

序列化的方式有这么多种,笔者不一一介绍了,挑一个大家都比较熟悉的,说明一下序列化的过程即可。

那我们就选择JavaSerialization(JDK原生的序列化方式)来做说明

3.1 序列化的过程

首先我们从ExchangeCodec.java来看下序列化的过程,这样可以更针对性的学习序列化的相关实现

public class ExchangeCodec extends TelnetCodec {
    protected void encodeRequest(Channel channel, ChannelBuffer buffer, Request req) throws IOException {
        // 获取序列化实现方式
        Serialization serialization = getSerialization(channel);
        ...
        // 通过serialize()方法获取ObjectOutput对象
        ObjectOutput out = serialization.serialize(channel.getUrl(), bos);
        if (req.isEvent()) {
            encodeEventData(channel, out, req.getData());
        } else {
            // 序列化请求data
            encodeRequestData(channel, out, req.getData(), req.getVersion());
        }
        // 刷新缓存
        out.flushBuffer();
        ...
    }
    
    protected void encodeRequestData(Channel channel, ObjectOutput out, Object data, String version) throws IOException {
        encodeRequestData(out, data);
    }
    
    protected void encodeRequestData(ObjectOutput out, Object data) throws IOException {
        // 最终通过调用ObjectOutput.writeObject()方法来实现序列化
        out.writeObject(data);
    }
}

通过分析可知:序列化的过程主要分为以下几个步骤:

* 获取序列化实现方式Serialization

* 通过Serialization.serialize() 方法获取ObjectOutput对象,后续都是通过ObjectOutput来操作

* 通过ObjectOutput.writeObject() 实现对请求对象的序列化

* 刷新ObjectOutput

通过以上序列化过程,我们参照JavaSerialization来观察其实现。

3.2 JavaSerialization

public class JavaSerialization implements Serialization {
	// Constants.java中定义  byte JAVA_SERIALIZATION_ID = 3;
    public byte getContentTypeId() {
        return JAVA_SERIALIZATION_ID;
    }

    @Override
    public String getContentType() {
        return "x-application/java";
    }

    // 获取ObjectOutput对象
    public ObjectOutput serialize(URL url, OutputStream out) throws IOException {
        return new JavaObjectOutput(out);
    }

    @Override
    public ObjectInput deserialize(URL url, InputStream is) throws IOException {
        return new JavaObjectInput(is);
    }
}

JavaSerialization中所使用的ObjectOutput实现类为JavaObjectOutput,后续的序列化操作都交由JavaObjectOutput来实现

3.2.1 JavaObjectOutput的使用

public class JavaObjectOutput extends NativeJavaObjectOutput {
    public JavaObjectOutput(OutputStream os) throws IOException {
        // 创建一个JDK原声的ObjectOutputStream对象,用于序列化对象
        super(new ObjectOutputStream(os));
    }

    public JavaObjectOutput(OutputStream os, boolean compact) throws IOException {
        super(compact ? new CompactedObjectOutputStream(os) : new ObjectOutputStream(os));
    }

    @Override
    public void writeUTF(String v) throws IOException {
        if (v == null) {
            getObjectOutputStream().writeInt(-1);
        } else {
            getObjectOutputStream().writeInt(v.length());
            getObjectOutputStream().writeUTF(v);
        }
    }

    // 主要看这个方法
    public void writeObject(Object obj) throws IOException {
        if (obj == null) {
            getObjectOutputStream().writeByte(0);
        } else {
            // 本质上直接调用了ObjectOutputStream.writeObject()方法
            getObjectOutputStream().writeByte(1);
            getObjectOutputStream().writeObject(obj);
        }
    }

    @Override
    public void flushBuffer() throws IOException {
        // 直接调用ObjectOutputStream.flush()进行刷新
        getObjectOutputStream().flush();
    }
}

总结:

JavaSerialization的实现并不算复杂,本质上,所有的序列化操作都交由JDK原声的ObjectOutputStream来执行。

而其他的序列化方式也是类似的,通过Serialization提供一个标准模板,具体的序列化操作都交由具体的实现来完成即可。

おすすめ

転載: blog.csdn.net/qq_26323323/article/details/121813844