网络协议
网络协议(应用层)是计算机网络中双方共同确定的交流语义,因为在通信双方都是以字节流的形式进行交换信息,所以需要一种约定对信息进行编解码操作。
常见的协议模式
既然协议是一种双方的约定,那如何制定这种通信的规范呢?在TCP协议中是以字节流的形式进行传输数据,并且为了提高数据传输效率,会对消息进行缓冲再一起发送。这就导致一个问题,每次发送的内容不一定是一个具有完整意义的报文。基于TCP协议之上的应用层协议需要知道一个完整的数据从何开始,从何结束,这就是所谓粘包和拆包。因此有以下常见处理方式:
- 定长
固定长度的协议报文,每次读取固定长度的字节进行解析。缺点是真实场景中报文长度都是不固定的,灵活性太差。 - 使用特殊字符
使用某个特殊字符作为结束标志,如果需要传的内容和特殊字符一样就很蛋疼。 - 协议头+payload
使用固定长度的消息头加可变长度的payload,在消息头中标识payload的长度,这种方式就比较好一些,也是常用的方式。
dubbo协议
dubbo协议是开源RPC框架DUBBO中自定义的私有化协议,使用协议头+payload的方式。
duboo协议大概就长这样,接下来我们来看一下DUBBO框架在消息的编解码这块是怎么处理的。DUBBO的模块划分很清晰,因此可以先从这个入口开始看Netty服务的启动。
org.apache.dubbo.remoting.transport.netty.NettyServer#doOpen
1 | ini复制代码@Override |
可以看出这里对Netty的编解码接口进行了适配,NettyCodecAdapter 类中组合了一个重要的接口 Codec2,这是DUBBO中编解码抽象的一个接口。还有两个内部类InternalEncoder 和 InternalDecoder 实现了Netty中的编解码的接口,接口方法中会调用Codec2的方法从而实现接口适配。
1 | ini复制代码private class InternalEncoder extends OneToOneEncoder { |
了解了接口的适配,接下来看看Codec2 接口
可以看到Codec2使用了SPI注解是一个扩展点,但是框架本身只写了dubbo协议一种编解码的实现,和dubbo协议相关的类有
- AbstractCodec // 抽象类,定义了一些静态方法
- ExchangeCodec // duboo协议编解码主要逻辑
- DubboCodec // 解析payload主要逻辑
- DubboCountCodec // 包装类,添加额外功能
接下详细看 org.apache.dubbo.remoting.exchange.codec.ExchangeCodec#decode(),解码过程
1 | ini复制代码protected Object decode(Channel channel, ChannelBuffer buffer, int readable, byte[] header) throws IOException { |
接下来开始解析payload,会根据header中的信息使用不同的对象序列化等策略,此时应该对应默认dubbo协议
org.apache.dubbo.rpc.protocol.dubbo.DubboCodec#decodeBody
1 | scss复制代码protected Object decodeBody(Channel channel, InputStream is, byte[] header) throws IOException { |
通过源码的追踪,我们了解了dubboo 协议大致如下
1 | lua复制代码header--16个字节 |
dubbo 协议看起来比较紧凑,在header中没有预留多余的字段,然后其实RPC 调用中还有很多参数是放在payload里的需要进行反序列化才能解析得到。通过阅读源码我们发现了一个问题,ExchangeCodec类从名字来看可以支持不同协议的转换,但是魔数和请求头的校验确固定了,并且其中的 decodeBody方法和子类DubboCodec中是一样的…总之能根据自己的业务场景设计出一个自定义协议还是挺有挑战性的。关于DUBBO3 中的主推的Triple 协议请听下回分解。
Triple 协议是 Dubbo3 推出的主力协议。Triple 意为第三代,通过 Dubbo1.0/ Dubbo2.0 两代协议的演进,以及云原生带来的技术标准化浪潮,Dubbo3 新协议 Triple 应运而生。
本文转载自: 掘金