Netty权威指南之编码和解码技术

哈哈 阅读:631 2021-03-31 22:28:44 评论:0

相关知识补充:

基于Java提供的对象输入和对象输出流ObjectInputStream和ObjectOutputStream,可以直接把Java对象作为可存储字节数组写入文件,也可以上传网络。

Java序列化的目的主要有两个:

1、网络传输

2、对象持久化

本章节主要介绍Netty的NIO网络开发,所以我们重点关注网络传输。当进行远程跨进程服务调用时,需要把被传输的Java对象转化为字节数组或者ByteBuffer对象。而但远程服务读取到字节数组或者ByteBuffer对象时,需要将其解码为发送的Java对象。这被称为Java解码和编码技术

Java序列化仅仅是Java解码和编码技术中的一种,由于它的种种缺陷,衍生出多种解码技术和解码框架。


本章学习计划

1、Java序列化缺点

2、业界流行的Java编码和解码框架


第一节:Java 序列化缺点

Java序列化从JDK1.1版本就已经开始提供,他不需要添加额外的类库。只需要实现java.io.serlizaable接口并生成序列ID即可。因此。它的诞生得到了广泛的运用。

但是在远程服务调用时(RPC),很少直接使用Java序列化进行消息的编码和解码工作,这又是什么原因造成的?

无法跨语言,是Java序列化最致命问题。对于跨进程的服务调用,服务者提供可能会使用C或者C++或是其他技术开发,但我们需要与异构语言进行交互时,Java序列化就难以胜任哒。

由于Java序列化技术是Java语言内部私有协议,其他语言并不支持,对于用户来说,它完全就是一个黑盒。对于Java序列化后的字节数组,其他语言无法进行反序列化,这严重阻碍了Java 序列化的应用。


java 序列化相关测试代码:

UserInfo.java

package com.nio.serlizable; 
 
import java.nio.ByteBuffer; 
 
/** 
 * Created by vixuan-008 on 2015/6/22. 
 */ 
public class UserInfo implements java.io.Serializable { 
 
    private static final long serialVersionUID=1L; 
 
    private String UserName; 
    private int UserId; 
 
    public UserInfo builderUserName(String UserName){ 
        this.UserName=UserName; 
        return this; 
    } 
    public UserInfo builderUserId(int UserId){ 
        this.UserId=UserId; 
        return this; 
    } 
 
    public String getUserName() { 
        return UserName; 
    } 
 
    public void setUserName(String userName) { 
        UserName = userName; 
    } 
 
    public int getUserId() { 
        return UserId; 
    } 
 
    public void setUserId(int userId) { 
        UserId = userId; 
    } 
 
    public byte[] codeC(){ 
        ByteBuffer buffer=ByteBuffer.allocate(1024); 
        byte[] value=this.UserName.getBytes(); 
        buffer.putInt(value.length); 
        buffer.put(value); 
        buffer.putInt(this.UserId); 
        buffer.flip(); 
        value=null; 
        byte[] result=new byte[buffer.remaining()]; 
        buffer.get(result); 
        return result; 
 
    } 
 
} 

UserInfoTest.java

package com.nio.serlizable; 
 
import java.io.ByteArrayOutputStream; 
import java.io.IOException; 
import java.io.ObjectOutputStream; 
 
/** 
 * Created by vixuan-008 on 2015/6/22. 
 */ 
public class UserInfoTest { 
    public static void main(String[] args) throws IOException{ 
        UserInfo info=new UserInfo(); 
        info.builderUserId(10).builderUserName("Welcome to Netty"); 
        ByteArrayOutputStream bos=new ByteArrayOutputStream(); 
        ObjectOutputStream os=new ObjectOutputStream(bos); 
        os.writeObject(info); 
        os.flush(); 
        os.close(); 
        byte[] b=bos.toByteArray(); 
        System.out.println("the jdk serlizable length is:"+b.length); 
        bos.close(); 
        System.out.println("-------------------"); 
        System.out.println("the byte array serlizable length is:"+info.codeC().length); 
 
    } 
} 

相关Java运行截图:


采用JDK序列化编码后二进制数据大小竟然是二进制编码的5.29倍。


评判一个编码框架的优劣,主要从以下几个方面入手:

1、是否支持跨语言,支持语言种类是否丰富。

2、编码后码流大小

3、编解码的性能

4、类库是小巧,API使用是否方便

5、使用者手工开发量和难度。


第二节:业界流行的Java编码和解码框架

(1)、Google的Protobuf

Protobuf 全称为Google Protocol Buffers,它由谷歌开源而来,在谷歌内部久经考验。它将数据结构以.proto文件进行描述,通过代码工具生成对应数据结构POJO对象和Protobuf相关属性和方法。

它的特点如下:

1、结构化数据存储(类似XML和JSON)

2、高效编码性能

3、语言无法、平台无法、拓展性好

4、官方支持Java、C++和Python

Protobuf 另一个吸引人的地方就是它的数据描述文件和代码生成机制,利用数据描述文件对数据结构进行说明优点如下:

1、文本化的数据结构语言,可以实现语言和平台无关,特别适合异构系统之间的集成。

2、通过标示字段顺序,可以实现协议前向兼容

3、自动代码生成,不需要手工编写同样的数据结构C++和Java版本

4、方便后续管理和维护,相比代码,结构化的数据结构更容易维护。


(2)、FaceBook的Thrift

Thrift源于大名鼎鼎的facebook之手,在2007facebook提交Apache基金会将Thrift作为一个开源项目,对于当时的facebook来说创造thrift是为了解决facebook系统中各系统间大数据量的传输通信以及系统之间语言环境不同需要跨平台的特性。所以thrift可以支持多种程序语言,例如: C++, C#, Cocoa, Erlang, Haskell, Java, Ocami, Perl, PHP, Python, Ruby, Smalltalk. 

在多种不同的语言之间通信thrift可以作为二进制的高性能的通讯中间件,支持数据(对象)序列化和多种类型的RPC服务。Thrift适用于程序对程 序静态的数据交换,需要先确定好他的数据结构,他是完全静态化的,当数据结构发生变化时,必须重新编辑IDL文件,代码生成,再编译载入的流程,跟其他IDL工具相比较可以视为是Thrift的弱项,Thrift适用于搭建大型数据交换及存储的通用工具,对于大型系统中的内部数据传输相对于JSONxml无论在性能、传输大小上有明显的优势。

Thrift 主要由5个部分组成

· 类型系统以及 IDL 编译器:负责由用户给定的 IDL 文件生成相应语言的接口代码 

· TProtocol:实现 RPC 的协议层,可以选择多种不同的对象串行化方式,如 JSON, Binary 

· TTransport:实现 RPC 的传输层,同样可以选择不同的传输层实现,如socket, 非阻塞的 socket, MemoryBuffer 等。 

· TProcessor:作为协议层和用户提供的服务实现之间的纽带,负责调用服务实现的接口。 

· TServer:聚合 TProtocol, TTransport 和 TProcessor 几个对象。

我们重点关注的是解编码框架,与之对应的就是TProtocol,由于Thrift的RPC服务调用和解编码框架绑定在一起,所以,我们通常使用Thrift的时候会采用RPC框架方式,但是,它的TProtocol解编码框架还是可以以类库的方式独立使用的。


与Protobuf比较类似的是,Thrift通过IDL描述接口和数据结构定义,它支持Java八中基础类型、Map、Set和List,支持可选和必选功能非常强大。因此可以定义数据结构中字段的顺序,所以它也可以支持协议前向兼容。

Thrift支持三种比较典型的解编码格式

1、通用二进制编码

2、压缩二进制解编码

3、优化可选字段压缩解编码


(3)、JBOSS Marshalling

JBoss Marshalling 是一个Java对象的序列化API包,修正了JDK自带的序列化包的很多问题,但又保持跟 java.io.Serializable 接口的兼容;同时增加了一些可调的参数和附加的特性,而这些参数和特性可通过工厂类进行配置。

相比传统Java 序列化,它的优点如下:

1、可插拔类解析器,提供更加便捷的类加载定制策略,通过一个接口,就可实现。

2、可插拔对象替换技术,不需要通过继承方式

3、可插拔预定义类缓存表,可以减小序列化字节数组长度,提升常用类型的序列化性能

4、无须实现java.io.serlizble 接口,就可实现序列化

5、通过缓存技术提升对象序列化的性能。



本章节的学习到这里就结束哒。





声明

1.本站遵循行业规范,任何转载的稿件都会明确标注作者和来源;2.本站的原创文章,请转载时务必注明文章作者和来源,不尊重原创的行为我们将追究责任;3.作者投稿可能会经我们编辑修改或补充。

关注我们

一个IT知识分享的公众号