JDK提供了ObjectOutputStream和ObjectInputStream,用於通過網絡對POJO的基本數據類型和圖進行序列化和反序列化。該API並不復雜,而且可以被應用於任何實現了java.io.Serializable接口的對象。但是它的性能也不是非常高效的。在這一節中,我們將看到Netty必須為此提供什么。
一、JDK序列化
如果你的應用程序必須要和使用了ObjectOutputStream和ObjectInputStream的遠程節點交互,並且兼容性也是你最關心的,那么JDK序列化將是正確的選擇。下表列出了Netty提供的用於和JDK進行互操作的序列化類。
JDK序列化編解碼器
CompatibleObjectDecoder和使用JDK序列化的非基於Netty的遠程節點進行互操作的解碼器
CompatibleObjectEncoder 和使用JDK序列化的非基於Netty的遠程節點進行互操作的編碼器
ObjectDecoder 構建於JDK 序列化之上的使用自定義的序列化來解碼的解碼器;當沒有其他的外部依賴時,它提供了速度上的改進。否則其他的序列化實現更加可取
ObjectEncoder 構建於JDK 序列化之上的使用自定義的序列化來編碼的編碼器;當沒有其他的外部依賴時,它提供了速度上的改進。否則其他的序列化實現更加可取
二、使用JBoss Marshalling 進行序列化
如果你可以自由地使用外部依賴,那么JBossMarshalling將是個理想的選擇:它比JDK序列化最多快3倍,而且也更加緊湊。在JBossMarshalling官方網站主頁上的概述中對它是這么定義的:
JBoss Marshalling是一種可選的序列化API,它修復了在JDK序列化API中所發現的許多問題,同時保留了與java.io.Serializable及其相關類的兼容性,並添加了幾個新的可調優參數以及額外的特性,所有的這些都是可以通過工廠配置(如外部序列化器、類/實例查找表、類解析以及對象替換等)實現可插拔的。
Netty通過下表所示的兩組解碼器/編碼器對為Boss Marshalling 提供了支持。第一組兼容只使用JDK序列化的遠程節點。第二組提供了最大的性能,適用於和使用JBoss Marshalling的遠程節點一起使用。
JBoss Marshalling 編解碼器
CompatibleMarshallingDecoder 與只使用JDK 序列化的遠程節點兼容
MarshallingDecoder 適用於使用JBoss Marshalling 的節點。這些類必須一起使用。
以下代碼清單展示了如何使用MarshallingDecoder 和MarshallingEncoder。同樣,幾乎只是適當地配置ChannelPipeline罷了。
使用JBoss Marshalling
public class MarshallingInitializer extends ChannelInitializer<Channel> { private final MarshallerProvider marshallerProvider; private final UnmarshallerProvider unmarshallerProvider; public MarshallingInitializer(UnmarshallerProvider unmarshallerProvider, MarshallerProvider marshallerProvider) { this.marshallerProvider = marshallerProvider; this.unmarshallerProvider = unmarshallerProvider; } @Override protected void initChannel(Channel channel) throws Exception { ChannelPipeline pipeline = channel.pipeline(); pipeline.addLast(new MarshallingDecoder(unmarshallerProvider));// 添加MarshallingDecoder以將ByteBuf 轉換為POJO pipeline.addLast(new MarshallingEncoder(marshallerProvider));// 添加MarshallingEncoder 以將POJO轉換為ByteBuf pipeline.addLast(new ObjectHandler());// 添加ObjectHandler,以處理普通的實現了Serializable 接口的POJO } public static final class ObjectHandler extends SimpleChannelInboundHandler<Serializable> { @Override public void channelRead0(ChannelHandlerContext channelHandlerContext, Serializable serializable) throws Exception { // Do something } } }
三、通過Protocol Buffers 序列化
Netty序列化的最后一個解決方案是利用Protocol Buffers的編解碼器,它是一種由Google公司開發的、現在已經開源的數據交換格式。可以在https://github.com/google/protobuf找到源代碼。
Protocol Buffers 以一種緊湊而高效的方式對結構化的數據進行編碼以及解碼。它具有許多的編程語言綁定,使得它很適合跨語言的項目。表11-10 展示了Netty為支持protobuf 所提供的ChannelHandler實現。
Protobuf 編解碼器
ProtobufDecoder 使用protobuf對消息進行解碼
ProtobufEncoder 使用protobuf對消息進行編碼
ProtobufVarint32FrameDecoder 根據消息中的Google Protocol Buffers 的“Base 128 Varints整型長度字段值動態地分割所接收到的ByteBuf
ProtobufVarint32LengthFieldPrepender 向ByteBuf 前追加一個Google Protocal Buffers 的“Base128 Varints”整型的長度字段值
在這里我們又看到了,使用protobuf只不過是將正確的ChannelHandler添加到ChannelPipeline 中,如下代碼所示。
使用protobuf
public class ProtoBufInitializer extends ChannelInitializer<Channel> { private final MessageLite lite; public ProtoBufInitializer(MessageLite lite) { this.lite = lite; } @Override protected void initChannel(Channel ch) throws Exception { ChannelPipeline pipeline = ch.pipeline(); pipeline.addLast(new ProtobufVarint32FrameDecoder());// 添加ProtobufVarint32FrameDecoder以分隔幀 pipeline.addLast(new ProtobufEncoder()); // 添加ProtobufEncoder以處理消息的編碼 pipeline.addLast(new ProtobufDecoder(lite));// 添加ProtobufDecoder以解碼消息 pipeline.addLast(new ObjectHandler());// 添加ObjectHandler以處理解碼消息 } public static final class ObjectHandler extends SimpleChannelInboundHandler<Object> { @Override public void channelRead0(ChannelHandlerContext ctx, Object msg) throws Exception { // Do something with the object } } }
由Netty專門的解碼器和編碼器所支持的不同的序列化選項:標准JDK序列化、JBoss Marshalling以及Google的Protocol Buffers。