Dubbo 序列化与反序列化

本文深入剖析Dubbo框架中的序列化机制,包括Hessian2序列化组件的使用,NettyClient启动流程,以及序列化与反序列化过程中的关键步骤。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Serialization

com.alibaba.dubbo.common.serialize.Serialization 是传输数据的序列化和反序列化的组件,首先来看看这个SPI类

@SPI("hessian2")
public interface Serialization {

    byte getContentTypeId();
    String getContentType();
    
    @Adaptive
    ObjectOutput serialize(URL url, OutputStream output) throws IOException;
    
    @Adaptive
    ObjectInput deserialize(URL url, InputStream input) throws IOException;
}

从注解上看序列化的默认组件采用hassian2

从配置文件里看,hassian2的实现类是

com.alibaba.dubbo.common.serialize.support.hessian.Hessian2Serialization

那么序列化和反序列化的动作是在哪里触发的呢,我们后面再讲Serialization的使用契机。

NettyClient

我们直接来到客户端启动与服务端创建连接的入口:com.alibaba.dubbo.remoting.transport.netty.NettyClient#doOpen

NettyClient是在DubboProtocol执行refer的时候根据URL初始化的,即客户端与服务端调用的传输层网络通信方式;

它的初始化调用链路:

com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol#refer
↓
com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol#getClients
↓
com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol#getSharedClient
↓
com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol#initClient
↓
com.alibaba.dubbo.remoting.exchange.Exchangers#connect(com.alibaba.dubbo.common.URL, com.alibaba.dubbo.remoting.exchange.ExchangeHandler)
↓
com.alibaba.dubbo.remoting.transport.netty.NettyTransporter#connect

最终会实例化一个NettyClient对象

他的默认类关系图

以下是netty客户端启动并连接服务端的代码,这里注入了一个 NettyCodecAdapter,

protected void doOpen() throws Throwable {
        NettyHelper.setNettyLoggerFactory();
        bootstrap = new ClientBootstrap(channelFactory);
        // config
        // @see org.jboss.netty.channel.socket.SocketChannelConfig
        bootstrap.setOption("keepAlive", true);
        bootstrap.setOption("tcpNoDelay", true);
        bootstrap.setOption("connectTimeoutMillis", getTimeout());
//NettyHandler 这里只是将this做了一个包装,它的作用类似一个包装器,真正做事情的还是this
        final NettyHandler nettyHandler = new NettyHandler(getUrl(), this);
        bootstrap.setPipelineFactory(new ChannelPipelineFactory() {
            public ChannelPipeline getPipeline() {
                NettyCodecAdapter adapter = new NettyCodecAdapter(getCodec(), getUrl(), NettyClient.this);
                ChannelPipeline pipeline = Channels.pipeline();
//向pipleline里注册handler
                pipeline.addLast("decoder", adapter.getDecoder());
                pipeline.addLast("encoder", adapter.getEncoder());
                pipeline.addLast("handler", nettyHandler);
                return pipeline;
            }
        });
    }

NettyCodecAdapter 

NettyCodecAdapter的构造函数有三个参数

public NettyCodecAdapter(Codec2 codec, URL url, com.alibaba.dubbo.remoting.ChannelHandler handler) {
        this.codec = codec;
        this.url = url;
        this.handler = handler;
        int b = url.getPositiveParameter(Constants.BUFFER_KEY, Constants.DEFAULT_BUFFER_SIZE);
        this.bufferSize = b >= Constants.MIN_BUFFER_SIZE && b <= Constants.MAX_BUFFER_SIZE ? b : Constants.DEFAULT_BUFFER_SIZE;
    }

 Codec2也是一个SPI接口,第二个是URL类型,第三个是netty的ChannelHandler;Codec2对象是在NettyClient父类AbstractEndpoint 的构造函数执行的时候初始化的

protected static Codec2 getChannelCodec(URL url) {
        String codecName = url.getParameter(Constants.CODEC_KEY, "telnet");
        if (ExtensionLoader.getExtensionLoader(Codec2.class).hasExtension(codecName)) {
            return ExtensionLoader.getExtensionLoader(Codec2.class).getExtension(codecName);
        } else {
            return new CodecAdapter(ExtensionLoader.getExtensionLoader(Codec.class).getExtension(codecName));
        }
    }

从代码可以看出Codec2对象是通过URL的codecName参数集合SPI机制获得的,以dubbo协议为例子这里会获得一个DubboCountCodec类型的Code2对象,而在DubboCountCodec类里实例化的时候会实例化一个DubboCodec的对象,而这个对象会做encode和decode的相关工作,DubboCountCodec只是对DubboCodec做了一层装饰,附加了记录日志等其他功能。

回到NettyCodecAdapter,在客户端启动的时候一共向PipleLine里注入了3个handler:

前面两个分别注入了“decoder”"encoder"两个handler,处理传输数据的序列化和反序列化就是两个了(最终处理者是Codec2的对象);

第三个handler主要是处理一些建立连接,关闭连接相关的事情(NettyClient也是ChannelHandler的子类,从上面的类继承关系图可以看出来)。

DubboCodec

先看看DubboCodec的类关系图

 

序列化

DubboCountCodec在接收到encode的调用时,会调用DubboCodec的encode方法(其实是父类ExchangeCodec的方法),这里会区分是请求的数据还是响应的数据而走不同的分支;

public void encode(Channel channel, ChannelBuffer buffer, Object msg) throws IOException {
        if (msg instanceof Request) {

//客户端发送请求,数据会被包装成Request,消费端做数据的编码
            encodeRequest(channel, buffer, (Request) msg);
        } else if (msg instanceof Response) {

//服务端响应消费端的请求,响应数据被包装成Response,服务端做数据编码
            encodeResponse(channel, buffer, (Response) msg);
        } else {
            super.encode(channel, buffer, msg);
        }
    }

这两个方法中都会去获得一个 Serialization 对象(后续方法就不分析了,序列化主要就是数据协议转换相关的操作):

Serialization serialization = getSerialization(channel); 最终调用的是:
public static Serialization getSerialization(URL url) {
        return ExtensionLoader.getExtensionLoader(Serialization.class).getExtension(url.getParameter(Constants.SERIALIZATION_KEY, Constants.DEFAULT_REMOTING_SERIALIZATION));
    }

如果参数中指定了 serialization 配置,则通过该配置获得 Serialization对象,否则获得默认的 hessian2对应的Hessian2Serialization;然后调用Serialization对象的 serialize 方法;

public class Hessian2Serialization implements Serialization {

    public static final byte ID = 2;

    public byte getContentTypeId() {
        return ID;
    }

    public String getContentType() {
        return "x-application/hessian2";
    }

//将OutputStream输出流包装成一个Hessian2ObjectOutput对象
    public ObjectOutput serialize(URL url, OutputStream out) throws IOException {
        return new Hessian2ObjectOutput(out);
    }

//将InputStream输入流对象包装成一个Hessian2ObjectOutput对象
    public ObjectInput deserialize(URL url, InputStream is) throws IOException {
        return new Hessian2ObjectInput(is);
    }

}


Hessian2ObjectOutput类是一个适配器,适配的类是Hessian2Output,主要做序列话相关的动作;有兴趣研究Hessian协议的可以看看Hessian2Output里序列化和反序列化的代码(不忍直视,大部分是位计算);

而Hessian2ObjectInput 类似,适配了Hessian2Input这个类。这个类主要做反序列化相关的动作。

反序列化

反序列化是在接收到消息后由handler发起,NettyCodecAdapter会实例化它的内部类

com.alibaba.dubbo.remoting.transport.netty.NettyCodecAdapter.InternalDecoder,这个decoder继承自SimpleChannelUpstreamHandler,主要负责了dubbo协议的解析、数据包半包、粘包等情况的处理

private class InternalDecoder extends SimpleChannelUpstreamHandler {

        private com.alibaba.dubbo.remoting.buffer.ChannelBuffer buffer =
                com.alibaba.dubbo.remoting.buffer.ChannelBuffers.EMPTY_BUFFER;

        @Override
        public void messageReceived(ChannelHandlerContext ctx, MessageEvent event) throws Exception {
            Object o = event.getMessage();
            if (!(o instanceof ChannelBuffer)) {
                ctx.sendUpstream(event);
                return;
            }

            ChannelBuffer input = (ChannelBuffer) o;
            int readable = input.readableBytes();
            if (readable <= 0) {
                return;
            }

            com.alibaba.dubbo.remoting.buffer.ChannelBuffer message;
            if (buffer.readable()) {
                if (buffer instanceof DynamicChannelBuffer) {
                    buffer.writeBytes(input.toByteBuffer());
                    message = buffer;
                } else {
                    int size = buffer.readableBytes() + input.readableBytes();
                    message = com.alibaba.dubbo.remoting.buffer.ChannelBuffers.dynamicBuffer(
                            size > bufferSize ? size : bufferSize);
                    message.writeBytes(buffer, buffer.readableBytes());
                    message.writeBytes(input.toByteBuffer());
                }
            } else {
                message = com.alibaba.dubbo.remoting.buffer.ChannelBuffers.wrappedBuffer(
                        input.toByteBuffer());
            }

            NettyChannel channel = NettyChannel.getOrAddChannel(ctx.getChannel(), url, handler);
            Object msg;
            int saveReaderIndex;

            try {
                // decode object.
                do {
                    saveReaderIndex = message.readerIndex();
                    try {
                        msg = codec.decode(channel, message);
                    } catch (IOException e) {
                        buffer = com.alibaba.dubbo.remoting.buffer.ChannelBuffers.EMPTY_BUFFER;
                        throw e;
                    }
                    if (msg == Codec2.DecodeResult.NEED_MORE_INPUT) {
                        message.readerIndex(saveReaderIndex);
                        break;
                    } else {
                        if (saveReaderIndex == message.readerIndex()) {
                            buffer = com.alibaba.dubbo.remoting.buffer.ChannelBuffers.EMPTY_BUFFER;
                            throw new IOException("Decode without read data.");
                        }
                        if (msg != null) {
                            DefaultChannelPipeline a;
                            Channels.fireMessageReceived(ctx, msg, event.getRemoteAddress());
                        }
                    }
                } while (message.readable());
            } finally {
                if (message.readable()) {
                    message.discardReadBytes();
                    buffer = message;
                } else {
                    buffer = com.alibaba.dubbo.remoting.buffer.ChannelBuffers.EMPTY_BUFFER;
                }
                NettyChannel.removeChannelIfDisconnected(ctx.getChannel());
            }
        }

        @Override
        public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) throws Exception {
            ctx.sendUpstream(e);
        }
    }

 

待续...

 

### Dubbo 序列化源码解析 Dubbo序列化机制位于 `org.apache.dubbo.common.serialize` 包下。主要类包括: - **Serialization**: 接口定义了序列化反序列化的标准操作。 - **ObjectOutput/ObjectInput**: 定义对象写入读取的具体实现。 对于 FastJSON Hessian2 这两种不同的序列化方式,对应的实现类分别为: #### FastJson 实现 FastJSON 是默认的序列化器,在 3.2 版本中被设置为默认选项[^1]。其核心逻辑由 `com.alibaba.fastjson.JSON` 类处理。 ```java public class FastjsonSerialization extends AbstractSerialization { @Override protected ObjectOutput createOutputStream(OutputStream out) throws IOException { return new FastjsonObjectOutput(out); } @Override protected ObjectInput createInputStream(InputStream in) throws IOException { return new FastjsonObjectInput(in); } } ``` #### Hessian2 实现 Hessian2 提供了一种二进制协议来优化网络传输效率。当配置文件指定了 `prefer-serialization: hessian2` 后,将会启用该序列化模式。 ```java public class Hessian2Serialization extends AbstractSerialization { @Override public byte getContentTypeId() { return CONTENT_TYPE_HESSIAN2; } @Override public String getContentType() { return MIME_APPLICATION_HESSIAN2; } @Override protected ObjectOutput createOutputStream(OutputStream out) throws IOException { return new Hessian2ObjectOutput(out); } @Override protected ObjectInput createInputStream(InputStream is) throws IOException { return new Hessian2ObjectInput(is); } } ``` 在遇到未注册异常的情况下,切换到 Hessian2 可以有效解决问题,因为 Hessian2 对于自定义类型的兼容性更好。 为了确保安全性并允许特定类进行反序列化,可以在配置文件中指定白名单路径 `security/serialize.allowlist` 或者调整安全策略模式[^2]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值