Netty编解码技术

编码技术

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

基于jdk默认的序列化机制可以避免操作底层的字节数组,从而提升开发效率。

Java序列化的两个目的

【1】网络传输

    当进行远程跨进程服务调用时,需要把被传输的Java对象编码为字节数组或者bytebuffer对象。二当远程服务读取到bytebuffer对象或者字节数组时,需要将其解码为发送时的Java对象,这种技术被称为Java对象编解码技术。

【2】对象持久化

Java序列化的缺点

【1】无法跨语言

Java序列化最为致命的问题。

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

扫描二维码关注公众号,回复: 2412770 查看本文章

【2】序列化以后的码流太大

package com.nio.seria;

import java.io.Serializable;
import java.nio.ByteBuffer;

public class UserInfo implements Serializable {

    private String userName;
    private Integer userId;

    public UserInfo buildUserName(String userName){
        this.userName = userName;
        return this;
    }

    public UserInfo buildUserId(int userId){
        this.userId = userId;
        return this;
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public Integer getUserId() {
        return userId;
    }

    public void setUserId(Integer userId) {
        this.userId = userId;
    }

    @Override
    public String toString() {
        return "UserInfo{" +
                "userName='" + userName + '\'' +
                ", 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;
    }
}
package com.nio.seria;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;

public class SeriaTest {

    public static void main(String[] args)throws IOException {
        UserInfo user = new UserInfo();
        user.buildUserId(100).buildUserName("mqs");
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectOutputStream os = new ObjectOutputStream(bos);
        os.writeObject(user);
        os.flush();
        os.close();
        byte[] b = bos.toByteArray();
        System.out.println("采用jdk序列化机制: " + b.length);

        System.out.println("采用二进制编码: " + user.codeC().length);


    }
}

以上结果发现采用jdk序列化机制编码后的二进制数组的大小是二进制编码的好多倍。

怎么评判一个编解码框架的优劣要考虑的因素:

  • 是否支持跨语言

  • 编码后的码流大小

  • 编解码的性能

  • 类库是否小巧,api使用是否方便

  • 使用者需要手工开发的工作量和难度

在同样的情况下,编码后的字节数组越大,存储的时候就越占空间,存储的硬件成本就越高,并且在网络传输时更占带宽,导致系统的吞吐量降低。

【3】序列化性能太低

package com.nio.seria;

import java.io.Serializable;
import java.nio.ByteBuffer;

public class UserInfo implements Serializable {

    private String userName;
    private Integer userId;

    public UserInfo buildUserName(String userName){
        this.userName = userName;
        return this;
    }

    public UserInfo buildUserId(int userId){
        this.userId = userId;
        return this;
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public Integer getUserId() {
        return userId;
    }

    public void setUserId(Integer userId) {
        this.userId = userId;
    }

    @Override
    public String toString() {
        return "UserInfo{" +
                "userName='" + userName + '\'' +
                ", userId=" + userId +
                '}';
    }

    public byte[] codeC(ByteBuffer buffer){
        buffer.clear();
        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;
    }
}
package com.nio.seria;

import java.io.ByteArrayOutputStream;
import java.io.ObjectOutputStream;
import java.nio.ByteBuffer;

public class SeriaTestXN {

    public static void main(String[] args)throws Exception {
       UserInfo info = new UserInfo();
       info.buildUserId(200).buildUserName("welcome to china study netty");

        System.out.println(info);

       int loop = 1000000;
        ByteArrayOutputStream bos = null;
        ObjectOutputStream os = null;
        long startTime = System.currentTimeMillis();
        for (int i = 0; i < loop; i++) {
           bos = new ByteArrayOutputStream();
           os =new ObjectOutputStream(bos);
           os.writeObject(info);
           os.flush();
           os.close();
           byte[] b = bos.toByteArray();
           bos.close();

        }
        long endTime = System.currentTimeMillis();
        System.out.println("jdk序列化编码耗时:" + (endTime - startTime) + "ms");


        System.out.println("---------------------------------------------------------");

        ByteBuffer buffer = ByteBuffer.allocate(1024);
        startTime = System.currentTimeMillis();
        for (int i = 0; i < loop; i++) {
            byte[] b = info.codeC(buffer);
        }
        endTime = System.currentTimeMillis();

        System.out.println("二进制编解码耗时: " + (endTime - startTime) + "ms");
    }
}

总结: 无论是序列化后的码流大小,还是序列化的性能,jdk默认的序列化机制表现都非常的差。因此,我们通常不会选择Java序列化作为远程跨节点调用的编解码框架。

业界主流的编解码框架

【1】Google的protobuf

【2】Facebook的thritf

【3】jboss的marshalling

猜你喜欢

转载自blog.csdn.net/wildwolf_001/article/details/81219665