Java在网络通信中应该如何选择合适的序列化框架?

news2024/11/17 5:34:10

前言

说到网络通信就会牵涉到数据的序列化与反序列化,现如今序列化框架也是层出不穷,比如FST、Kryo、ProtoBuffer、Thrift、Hessian、Avro、MsgPack等等,有的人可能会有疑问,为什么市面上有这么多框架,JDK不是已经有自带的Serializable序列化接口吗?很遗憾地说出这个事实,作为JDK自带地序列化机制,无论是在时间还是空间上的性能不尽人意,但凡时间或者空间上性能优越一点,也不至于让人诟病这么久。所以也就出现了这么多序列化框架,另外即便JDK序列化可以实现,但是无法在跨语言的网络通信中表现出色,除非在多个语言端各自定义好每一个对象,但是这样做无疑效率是最低的,需要提前定义的对象太多了,人力明显不够用。所以也就出现了以ProtoBuffer、Kryo、Thrift等支持跨语言的框架,接下来让我们看下各个框架之间的比较,有比较才能在业务中有更多的选择

序列化框架通用性
JDK Serializer只适用于Java
FST只适用于Java
Kryo主要适用于Java(可复杂支持多种语言)
Protocol buffer支持多种语言
Thrift支持多种语言
Hessian支持多种语言
Avro支持多种语言
MsgPack支持多种语言

性能比较

在这里插入图片描述
在这里插入图片描述

从时间和空间复杂度上来说,kryo的表现是其他序列化框架当中比较好的,序列化之后的大小空间上以及序列化/反序列化时间都不错,但是它没有那么直接不支持跨语言。

接下来我们再来看看针对大小数据序列化的结果,数据的大小也会影响序列化的结果。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

分析结果

如果你的系统架构设计中设计到了多语言,那么Proto Buffer和MsgPack,avro将会是个不错的选择,当你的系统是Java时,还可以考虑kryo,它的序列化和反序列化时间相对均衡。

使用示例

    <dependencies>
        <dependency>
            <groupId>io.netty</groupId>
            <artifactId>netty-all</artifactId>
            <version>4.1.42.Final</version>
        </dependency>

        <dependency>
            <groupId>org.msgpack</groupId>
            <artifactId>msgpack</artifactId>
            <version>0.6.12</version>
        </dependency>

        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-api</artifactId>
            <version>1.7.30</version>
            <scope>compile</scope>
        </dependency>

        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-core</artifactId>
            <version>1.2.4</version>
        </dependency>

        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>1.2.4</version>
        </dependency>

        <dependency>
            <groupId>com.itextpdf</groupId>
            <artifactId>itextpdf</artifactId>
            <version>5.5.8</version>
        </dependency>
        <dependency>
            <groupId>org.bouncycastle</groupId>
            <artifactId>bcprov-jdk15on</artifactId>
            <version>1.49</version>
            <type>jar</type>
            <scope>compile</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.bouncycastle</groupId>
            <artifactId>bcpkix-jdk15on</artifactId>
            <version>1.49</version>
            <type>jar</type>
            <scope>compile</scope>
            <optional>true</optional>
        </dependency>

        <dependency>
            <groupId>de.javakaffee</groupId>
            <artifactId>kryo-serializers</artifactId>
            <version>0.42</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13.1</version>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>com.google.protobuf</groupId>
            <artifactId>protobuf-java</artifactId>
            <version>2.6.1</version>
        </dependency>
    </dependencies>

1.ProtoBuffer

Proto文件如何生成JavaProto对象?这篇博客已经介绍过了ProtoBuffer了,这里不再赘述,有兴趣的小伙伴可以看看

2.Kryo

实体类

package serializable.protogenesis;

import org.msgpack.annotation.Message;

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

@Message
public class UserInfo implements Serializable {
    /**
     * 默认序列号
     */
    private static final long serialVersionUID = 7627113094707995002L;
    
    private String userName;
    
    private int userID;

    public String getUserName() {
        return userName;
    }

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

    public int getUserID() {
        return userID;
    }

    public UserInfo setUserID(int userID) {
        this.userID = userID;
        return this;
    }
    
    // 自行序列化
    public byte[] codeC() {
        ByteBuffer buffer = ByteBuffer.allocate(1024);
        // userName转换为字节数组value
        byte[] value = this.userName.getBytes();
        // 写入字节数组value的长度
        buffer.putInt(value.length);
        // 写入字节数组value的值
        buffer.put(value);
        // 写入userID的值
        buffer.putInt(this.userID);
        // 准备读取buffer中的数据
        buffer.flip();
        value = null;
        byte[] result = new byte[buffer.remaining()];
        // buffer中的数据写入字节数组并作为结果返回
        buffer.get(result);
        return result;
        
    }
    
    // 自行序列化方法2
    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;
    }


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


KryoSerializer

package adv.kryocodec;

import com.esotericsoftware.kryo.Kryo;
import com.esotericsoftware.kryo.io.Input;
import com.esotericsoftware.kryo.io.Output;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufInputStream;

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

public class KryoSerializer {
    
    private static Kryo kryo = KryoFactory.createKryo();
    
    // 序列化
    public static void serialize(Object object, ByteBuf out) {
        long start = System.currentTimeMillis();
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        Output output = new Output(baos);
        kryo.writeClassAndObject(output, object);
        
        output.flush();
        output.close();

        byte[] b = baos.toByteArray();
        try {
            baos.flush();
            baos.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        
        out.writeBytes(b);

        long end = System.currentTimeMillis();
        System.out.println("The Kryo serializable length is "+ b.length +", serialize time is :" + (end - start));
    }
    
    // 序列化为一个字节数组,主要用在消息摘要上
    public static byte[] obj2Bytes(Object object) {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        Output output = new Output(baos);
        kryo.writeClassAndObject(output, object);
        output.flush();
        output.close();

        byte[] b = baos.toByteArray();
        try {
            baos.flush();
            baos.close();
        } catch (IOException e) {
            e.printStackTrace();
        }
        
        return b;
    }
    
    
    public static Object deserialize(ByteBuf out) {
        if (out == null) {
            return null;
        }
        
        Input input = new Input(new ByteBufInputStream(out));
        return kryo.readClassAndObject(input);
    }
}

KryoFactory
在这个工厂中我们提前注册了很多序列化器,这样会加快我们序列化的速度

package adv.kryocodec;

import com.esotericsoftware.kryo.Kryo;
import com.esotericsoftware.kryo.serializers.DefaultSerializers;
import de.javakaffee.kryoserializers.*;

import java.lang.reflect.InvocationHandler;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.net.URI;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Pattern;

public class KryoFactory {
    
    
    public static Kryo createKryo() {
        Kryo kryo = new Kryo();
        kryo.setRegistrationRequired(false);
        kryo.register(Arrays.asList("").getClass(),  new ArraysAsListSerializer());
        kryo.register(GregorianCalendar.class, new GregorianCalendarSerializer());
        kryo.register(InvocationHandler.class, new JdkProxySerializer());
        kryo.register(BigDecimal.class, new DefaultSerializers.BigDecimalSerializer());
        kryo.register(BigInteger.class, new DefaultSerializers.BigIntegerSerializer());
        kryo.register(Pattern.class, new RegexSerializer());
        kryo.register(BitSet.class, new BitSetSerializer());
        kryo.register(URI.class, new URISerializer());
        kryo.register(UUID.class, new UUIDSerializer());
        
        UnmodifiableCollectionsSerializer.registerSerializers(kryo);
        SynchronizedCollectionsSerializer.registerSerializers(kryo);
        
        kryo.register(HashMap.class);
        kryo.register(ArrayList.class);
        kryo.register(LinkedList.class);
        kryo.register(HashSet.class);
        kryo.register(TreeSet.class);
        kryo.register(Hashtable.class);
        kryo.register(Date.class);
        kryo.register(Calendar.class);
        kryo.register(ConcurrentHashMap.class);
        kryo.register(SimpleDateFormat.class);
        kryo.register(GregorianCalendar.class);
        kryo.register(Vector.class);
        kryo.register(BitSet.class);
        kryo.register(StringBuffer.class);
        kryo.register(StringBuilder.class);
        
        kryo.register(Object.class);
        kryo.register(Object[].class);
        kryo.register(String[].class);
        kryo.register(byte[].class);
        kryo.register(char[].class);
        kryo.register(int[].class);
        kryo.register(float[].class);
        kryo.register(double[].class);
        
        return kryo;
    }
}

TestPerform

package adv.kryocodec;

import io.netty.buffer.ByteBuf;
import io.netty.buffer.Unpooled;
import serializable.protogenesis.UserInfo;

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

public class TestPerform {

    public static void main(String[] args) throws IOException {
        UserInfo info = new UserInfo();
        info.setUserID(100).setUserName("Hello World");
        long start = System.currentTimeMillis();
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectOutputStream os = new ObjectOutputStream(bos);
        os.writeObject(info);
        os.flush();
        os.close();

        byte[] b = bos.toByteArray();
        long end = System.currentTimeMillis();
        System.out.println("The JDK serializable length is :" + b.length + ", time is :" + (end - start));
        ByteBuf sendBuf = Unpooled.buffer();
        KryoSerializer.serialize(info, sendBuf);
        UserInfo deserialize = (UserInfo)KryoSerializer.deserialize(sendBuf);
        System.out.println("deserialize = " + deserialize.toString());
    }
}

在这里插入图片描述

3.MsgPack

TestMsgPackPerform

package serializable.msgpack;

import org.msgpack.MessagePack;
import serializable.protogenesis.UserInfo;

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

public class TestMsgPackPerform {

    public static void main(String[] args) throws IOException {
        UserInfo info = new UserInfo();
        info.setUserID(100).setUserName("Hello World");
        long start = System.currentTimeMillis();
        ByteArrayOutputStream bos = new ByteArrayOutputStream();
        ObjectOutputStream os = new ObjectOutputStream(bos);
        os.writeObject(info);
        os.flush();
        os.close();

        byte[] b = bos.toByteArray();
        long end = System.currentTimeMillis();
        System.out.println("The JDK serializable length is :" + b.length + ", time is :" + (end - start));

        
        MessagePack messagePack = new MessagePack();
        byte[] bytes = messagePack.write(info);

        UserInfo read = messagePack.read(bytes, UserInfo.class);
        System.out.println("The MsgPack serializable length is :" + bytes.length);
        System.out.println("read = " + read);
    }
}

在这里插入图片描述
由于在序列化时会申请ByteBuf来操作,所以这个申请内存(无论是堆内存还是直接内存)都是需要耗费点时间的,所以不能把这个时间也算进去,这样我们得到的时间是不准的,只要在大量的序列化情况下才能看出效果。另外注意在实体类上加@Message这个注解,否则会报错

附录:NIO序列化

我们都知道NIO中也有ByteBuffer可以用来做序列化
TestUserInfo

public class TestUserInfo {

    public static void main(String[] args) throws IOException {
        UserInfo info = new UserInfo();
        info.setUserID(100).setUserName("Hello World");
        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 serializable length is :" + b.length);
        bos.close();
        System.out.println("-------------------------------------------");
        System.out.println("the byte array serializable length is :" + info.codeC().length);

    }
}

PerformTestUserInfo

package serializable.protogenesis;

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

public class PerformTestUserInfo {

    public static void main(String[] args) throws IOException {
        UserInfo info = new UserInfo();
        info.setUserID(100).setUserName("Hello World");
        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[] bytes = bos.toByteArray();
            bos.close();
        }
        long endTime = System.currentTimeMillis();
        System.out.println("The JDK serializable cost time is :" + (endTime - startTime) + "ms");
        System.out.println("---------------------------------");
        
        ByteBuffer buffer = ByteBuffer.allocate(1024);
        startTime = System.currentTimeMillis();
        for (int i = 0; i < loop; i++) {
            byte[] bytes = info.codeC(buffer);
        }
        endTime = System.currentTimeMillis();

        System.out.println("The byte array serializable cost time is :" + (endTime - startTime) + "ms");
        

    }
}

在这里插入图片描述
在这里插入图片描述
NIO原生的序列化机制也有很不错的机制

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1423514.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

LeetCode刷题日志-117填充每个节点的下一个右侧指针II

二叉树的题目&#xff0c;我认为二叉树必须要掌握递归的三种遍历算法&#xff0c;以及层序遍历算法才能做二叉树题目。这道题目我的想法是&#xff1a; 因为在二叉树每一层中&#xff0c;next指针指向的是的当前节点的右边的节点&#xff0c;所以&#xff0c;使用层序遍历&…

中国建设银行,这年终奖噶噶高!!!!(含算法原题)

国企年终 今天刷到一个近期帖子:「中国建设银行&#xff0c;这年终奖噶噶高!!!!」 先撇去具体内容不看&#xff0c;能在自然年的 月初&#xff0c;就把去年的奖金发了的企业&#xff0c;首先值得一个点赞。 再细看内容&#xff0c;年终奖是一个 字头的 位数。 由于国企通常没…

springboot-前后端分离——第二篇

本篇主要介绍一个发送请求的工具—postman&#xff0c;然后对请求中的参数进行介绍&#xff0c;例如简单参数、实体参数、数组参数、集合参数、日期类型参数以及json类型参数&#xff0c;对这些参数接收进行总结。最后对响应数据进行介绍&#xff0c;使用统一响应结果返回浏览器…

知识蒸馏(paper翻译)

paper&#xff1a;Distilling the Knowledge in a Neural Network 摘要&#xff1a; 提高几乎所有机器学习算法性能的一个非常简单的方法是在相同的数据上训练许多不同的模型&#xff0c;然后对它们的预测进行平均[3]。 不幸的是&#xff0c;使用整个模型集合进行预测非常麻烦…

养老院|基于Springboot的养老院管理系统设计与实现(源码+数据库+文档)

养老院管理系统目录 目录 基于Springboot的养老院管理系统设计与实现 一、前言 二、系统功能设计 三、系统实现 1、老人信息管理 2、家属信息管理 3、公告类型管理 4、公告信息管理 四、数据库设计 1、实体ER图 五、核心代码 六、论文参考 七、最新计算机毕设选…

2023强网杯复现

强网先锋 SpeedUp 要求2的27次方的阶乘的逐位之和 在A244060 - OEIS 然后我们将4495662081进行sha256加密 就得到了flag flag{bbdee5c548fddfc76617c562952a3a3b03d423985c095521a8661d248fad3797} MISC easyfuzz 通过尝试输入字符串判断该程序对输入字符的验证规则为9…

计算机设计大赛 深度学习 python opencv 动物识别与检测

文章目录 0 前言1 深度学习实现动物识别与检测2 卷积神经网络2.1卷积层2.2 池化层2.3 激活函数2.4 全连接层2.5 使用tensorflow中keras模块实现卷积神经网络 3 YOLOV53.1 网络架构图3.2 输入端3.3 基准网络3.4 Neck网络3.5 Head输出层 4 数据集准备4.1 数据标注简介4.2 数据保存…

万物简单AIoT 端云一体实战案例学习 之 智能小车

学物联网,来万物简单IoT物联网!! 下图是本案的3步导学,每个步骤中实现的功能请参考图中的说明。 1、简介 1.1、背景 市面上各种遥控的小车很多,小车的性能不同具备的能力也不一样,大概实现的逻辑就是通过遥控器控制小车的前进、后退、左转或者右转。遥控小车具备一定…

精通Python第14篇—Pyecharts神奇妙笔,绘制多彩词云世界

文章目录 安装Pyecharts基本的词云图绘制自定义词云图样式多种词云图合并高级词云图定制与交互1. 添加背景图片2. 添加交互效果 使用自定义字体和颜色从文本文件生成词云图总结&#xff1a; 在数据可视化领域&#xff0c;词云图是一种极具表现力和趣味性的图表&#xff0c;能够…

IDEA 取消参数名称提示、IDEA如何去掉变量类型提醒

一、IDEA 取消参数名称显示 取消显示形参名提示 例如这样的提示信息 二、解决方法 1、File—>Setting–>Editor—>Inlay Hints—>Java 去掉 Show Parameter hints for 前面的勾即可&#xff0c;然后Apply—>Ok 2、右键Disable Hints

java 图书管理系统 spring boot项目

java 图书管理系统ssm框架 spring boot项目 功能有管理员模块&#xff1a;图书管理&#xff0c;读者管理&#xff0c;借阅管理&#xff0c;登录&#xff0c;修改密码 读者端&#xff1a;可查看图书信息&#xff0c;借阅记录&#xff0c;登录&#xff0c;修改密码 技术&#…

离散数学5

集合的基本概念 集合间的关系 特殊集合 集合的运算 以上都是高一学过的内容。 有穷集的计数&#xff08;容斥定理&#xff09; 序偶与集合的笛卡尔积 二元关系及其表示法 二元关系的性质 前件<x,y>,<y,z>后件<x,z>通过前件能推出后件&#xff0c;只有前真…

【51单片机系列】应用设计——8路抢答器的设计

51单片机应用——8路抢答器设计 文章设计文件及代码&#xff1a;资源链接。 文章目录 要求&#xff1a;设计思路软件设计仿真结果 要求&#xff1a; &#xff08;1&#xff09; 按下”开始“按键后才开始抢答&#xff0c;且抢答允许指示灯亮&#xff1b; &#xff08;2&…

空间域:空间组学的耶路撒冷

文章目录 环境配置与数据SquidpySpaGCN将基因表达和组织学整合到一个图上基因表达数据质控与预处理SpaGCN的超参优化空间域 参考文献 空间组学不能没有空间域&#xff0c;就如同蛋白质不能没有结构域。 摘要&#xff1a; 空间域是反映细胞在基因表达方面的相似性以及空间邻近性…

vulnhub靶场之Matrix-Breakout 2 Morpheus

一.环境搭建 1.靶场描述 This is the second in the Matrix-Breakout series, subtitled Morpheus:1. It’s themed as a throwback to the first Matrix movie. You play Trinity, trying to investigate a computer on the Nebuchadnezzar that Cypher has locked everyone…

微信小程序如何实现实时显示输入内容

如下所示&#xff0c;在许多场景中需要实时显示用户输入&#xff0c;具体实现见下文。 .wxml <input type"text" placeholder"请输入{{item.value}}(必填)" style"width:80%;" bindinput"get_required_value" data-info"{{it…

HarmonyOS应用开发者基础认证考试答案

HarmonyOS应用开发者基础认证考试答案 一、判断题 1.Ability是系统调度应用的最小单元&#xff0c;是能够完成一个独立功能的组件。一个应用可以包含一个或多个Ability。 正确(True) 2.所有使用Component修饰的自定义组件都支持onPageShow&#xff0c;onBackPress和onPageHide…

linux -- 中断管理 -- softirq机制

softirq的起始 do_IRQ();--> irq_enter(); //HARDIRQ部分的开始 更新系统中的一些统计量 标识出HARDIRQ上下文--> generic_irq_handler(); --> irq_exit(); //softirq部分的起始irq_exit /** Exit an interrupt context. Process softirqs if needed and possibl…

MOS栅极驱动和运放所需注意的关键参数

FD6288Q_&#xff08;JSMSEMI(杰盛微)&#xff09;FD6288Q中文资料_价格_PDF手册-立创电子商城 (szlcsc.com) MOS栅极驱动芯片&#xff1a; 自举电路&#xff1a; 电容的两个重要参数&#xff1a; ESR&#xff08;等效串联电阻&#xff09;和ESL&#xff08;等效串联电感&…

基于javaEE的社区食堂管理-计算机毕业设计源码48691

摘 要 随着餐饮业强劲发展的趋势&#xff0c;企业对食堂的管理也更加严格。面对材料成本的提高&#xff0c;人才资源匮乏&#xff0c;租金成本提高等问题&#xff0c;企业如何改善食堂管理系统将成为挑战。 一个高效便捷的食堂管理系统&#xff0c;能为食堂管理者带来极大的便利…