IT评测·应用市场-qidao123.com

标题: 【序列化与反序列化】关于序列化与反序列化MessagePack的实践 [打印本页]

作者: 拉不拉稀肚拉稀    时间: 2023-7-3 16:25
标题: 【序列化与反序列化】关于序列化与反序列化MessagePack的实践
1.什么是序列化与反序列化?

聊到序列化与反序列化,先看看这个这个是什么或者是干嘛的

定义:序列化是指把对象转换为字节序列的过程;反序列化是指把字节序列恢复为对象的过程;
一般都会知道有两个目的:对象持久化和网络传输。
总结:序列化的目的是将对象变成字节序列,这样一来方便持久化存储到磁盘,避免程序运行结束后对象就从内存里消失,另外字节序列也更便于网络运输和传播
2.选择序列化技术有哪些维度

这里并不是说哪个是最好,只能说是各有千秋,所以面对不同的背景采用不同的技术方案。
在同等情况下,编码后的字节数组越大,存储占空间,存储硬件成本高,网络传输时也占带宽,导致系统的吞吐量降低。
3.为什么采用字节序列MessagePack,有什么依据?

这个要结合当时的项目背景了。当时的项目痛点是:
所以,存在MessagePack也有不好的地方,如果是针对业务变化比较多,那就不适合,需要比较不同的版本,然后选择不同版本去解析。
我在京东內部文章有看到,引用的MessagePack反序列化出现了一些线上的问题,原因是:新增了一些字段导致反序列化失败
这里对比了JDK(不支持跨语言),JSON,Protobuf,MessagePack几种序列化的方式。
当然,还有其他的XML、Hessian、Kryo(不支持跨语言)、FST(不支持跨语言)Thrift等。
4.JDK序列化

JDK序列化是Java默认自带的序列化方式。在Java中,一个对象要想实现序列化,实现Serializable接口或者Externalizable接口即可。其中Externalizable接口继承自Serializable接口。
  1. public class Person implements Serializable {
  2.     private static final long serialVersionUID = 1L;
  3.     private String name = null;
  4.     private Integer age = null;
  5.     private SerializeDemo01.Sex sex;
  6.         //....
  7. }
复制代码
serialVersionUID版本控制的作用
serialVersionUID必须保证实体类在序列化前后严格一致,否则将会导致无法反序列化。
JDK默认的序列化机制。需要实现java.io.Serializable接口
JDK默认的序列化机制很差。所以我们通常不会选择Java默认序列化这种
5.JSON序列化

json格式也是常见的一种,但是在json在解析的时候非常耗时,而且json结构非常占内存。JSON不适合存数字,特别是DOUBLE
这里基于Fastjson ,加载maven依赖
  1. <dependency>
  2.     <groupId>com.alibaba</groupId>
  3.     <artifactId>fastjson</artifactId>
  4.     <version>1.2.70</version>
  5. </dependency>
复制代码
序列化对象
利用JSON.toJSONString方法序列化对象:
  1. UserVO user = ...;String text = JSON.toJSONString(user);
复制代码
序列化数组
利用JSON.toJSONString方法序列化数组:
  1. UserVO[] users = ...;String text = JSON.toJSONString(users);
复制代码
序列化集合
利用JSON.toJSONString方法序列化集合(继承至Collection,比如List、Set等集合):
  1. List<UserVO> userList = ...;String text = JSON.toJSONString(userList);
复制代码
序列化映射
利用JSON.toJSONString方法序列化映射:
  1. Map<Long, UserVO> userMap = ...;String text = JSON.toJSONString(userMap, SerializerFeature.MapSortField);
复制代码
其中,为了保证每次序列化的映射字符串一致,需要指定序列化参数MapSortField进行排序。
序列化模板对象
利用JSON.toJSONString方法序列化模板对象:
  1. Result<UserVO> result = ...;String text = JSON.toJSONString(result);
复制代码
代码
  1. package com.nateshao.source.code.serializable.json_Serializable.serializable;
  2. import com.alibaba.fastjson.annotation.JSONField;
  3. /**
  4. * @date Created by 邵桐杰 on 2023/2/25 17:11
  5. * @微信公众号 千羽的编程时光
  6. * @博客 https://nateshao.gitlab.io
  7. * @GitHub https://github.com/nateshao
  8. * Description:
  9. */
  10. public class User {
  11.     /**
  12.      * @JSONField 作用:自定义对象属性所对应的 JSON 键名
  13.      * @JSONField 的作用对象:
  14.      * 1. Field
  15.      * 2. Setter 和 Getter 方法
  16.      * 注意:
  17.      * 1. 若属性是私有的,必须要有 set 方法,否则反序列化会失败。
  18.      * 2. 若没有 @JSONField 注解,则直接使用属性名。
  19.      */
  20.     @JSONField(name = "NAME")
  21.     private String name;
  22.     @JSONField(name = "AGE")
  23.     private int age;
  24.     public User(String name, int age) {
  25.         this.name = name;
  26.         this.age = age;
  27.     }
  28.     public String getName() {
  29.         return name;
  30.     }
  31.     public void setName(String name) {
  32.         this.name = name;
  33.     }
  34.     public int getAge() {
  35.         return age;
  36.     }
  37.     public void setAge(int age) {
  38.         this.age = age;
  39.     }
  40. }
复制代码
序列化ObjectTest
  1. package com.nateshao.source.code.serializable.json_Serializable.serializable;
  2. import com.alibaba.fastjson.JSON;
  3. import org.junit.jupiter.api.BeforeAll;
  4. import org.junit.jupiter.api.DisplayName;
  5. import org.junit.jupiter.api.Test;
  6. import java.util.ArrayList;
  7. import java.util.HashMap;
  8. import java.util.List;
  9. import java.util.Map;
  10. /**
  11. * @date Created by 邵桐杰 on 2023/2/25 17:12
  12. * @微信公众号 千羽的编程时光
  13. * @博客 https://nateshao.gitlab.io
  14. * @GitHub https://github.com/nateshao
  15. * Description:
  16. */
  17. public class ObjectTest {
  18.     private static List<User> userList = new ArrayList<User>();
  19.     @BeforeAll
  20.     public static void setUp() {
  21.         userList.add(new User("千羽", 18));
  22.         userList.add(new User("千寻", 19));
  23.     }
  24.     @DisplayName("序列化对象")
  25.     @Test
  26.     public void testObjectToJson() {
  27.         String userJson = JSON.toJSONString(userList.get(0));
  28.         System.out.println(userJson);  // {"AGE":18,"NAME":"千羽"}
  29.     }
  30.     @DisplayName("序列化集合")
  31.     @Test
  32.     public void testListToJson() {
  33.         String userListJson = JSON.toJSONString(userList);
  34.         System.out.println(userListJson);  // [{"AGE":18,"NAME":"千羽"},{"AGE":19,"NAME":"千寻"}]
  35.     }
  36.     @DisplayName("序列化数组")
  37.     @Test
  38.     public void testArrayToJson() {
  39.         User[] userArray = new User[5];
  40.         userArray[0] = new User("zhangsan", 20);
  41.         userArray[1] = new User("lisi", 21);
  42.         String userArrayJson = JSON.toJSONString(userArray);
  43.         System.out.println(userArrayJson);  // [{"AGE":20,"NAME":"zhangsan"},{"AGE":21,"NAME":"lisi"},null,null,null]
  44.     }
  45.     @DisplayName("序列化映射")
  46.     @Test
  47.     public void testMapToJson() {
  48.         Map<Integer, User> userMap = new HashMap<Integer, User>();
  49.         userMap.put(1, new User("xiaotie", 10));
  50.         userMap.put(2, new User("xiaoliu", 11));
  51.         String userMapJson = JSON.toJSONString(userMap);
  52.         System.out.println(userMapJson);  // {1:{"AGE":10,"NAME":"xiaotie"},2:{"AGE":11,"NAME":"xiaoliu"}}
  53.     }
  54. }
复制代码
反序列化UN_ObjectTest
  1. package com.nateshao.source.code.serializable.json_Serializable.un_serializable;
  2. import com.alibaba.fastjson.JSON;
  3. import com.alibaba.fastjson.TypeReference;
  4. import org.junit.jupiter.api.DisplayName;
  5. import org.junit.jupiter.api.Test;
  6. import java.util.Arrays;
  7. import java.util.List;
  8. import java.util.Map;
  9. import java.util.Set;
  10. /**
  11. * @date Created by 邵桐杰 on 2023/2/25 17:16
  12. * @微信公众号 千羽的编程时光
  13. * @博客 https://nateshao.gitlab.io
  14. * @GitHub https://github.com/nateshao
  15. * Description:
  16. */
  17. public class UN_ObjectTest {
  18.     @DisplayName("反序列化对象")
  19.     @Test
  20.     public void testJsonToObject() {
  21.         String text = "{"age":18,"name":"千羽"}";
  22.         User user = JSON.parseObject(text, User.class);
  23.         System.out.println(user);  // User{name='千羽', age=18}
  24.     }
  25.     @DisplayName("反序列化数组")
  26.     @Test
  27.     public void testJsonToArray() {
  28.         String text = "[{"age":18,"name":"千羽"}, {"age":19,"name":"千寻"}]";
  29.         User[] users = JSON.parseObject(text, User[].class);
  30.         System.out.println(Arrays.toString(users));  // [User{name='千羽', age=18}, User{name='千寻', age=19}]
  31.     }
  32.     @DisplayName("反序列化集合")
  33.     @Test
  34.     public void testJsonToCollection() {
  35.         String text = "[{"age":18,"name":"千羽"}, {"age":19,"name":"千寻"}]";
  36.         // List 集合
  37.         List<User> userList = JSON.parseArray(text, User.class);
  38.         System.out.println(Arrays.toString(userList.toArray()));  // [User{name='千羽', age=18}, User{name='千寻', age=19}]
  39.         // Set 集合
  40.         Set<User> userSet = JSON.parseObject(text, new TypeReference<Set<User>>() {
  41.         });
  42.         System.out.println(Arrays.toString(userSet.toArray()));  // [User{name='千寻', age=19}, User{name='千羽', age=18}]
  43.     }
  44.     @DisplayName("反序列化映射")
  45.     @Test
  46.     public void testJsonToMap() {
  47.         String text = "{1:{"age":18,"name":"千羽"}, 2:{"age":19,"name":"千寻"}}";
  48.         Map<Integer, User> userList = JSON.parseObject(text, new TypeReference<Map<Integer, User>>() {
  49.         });
  50.         for (Integer i : userList.keySet()) {
  51.             System.out.println(userList.get(i));
  52.         }
  53.         /*
  54.             User{name='千羽', age=18}
  55.             User{name='千寻', age=19}
  56.          */
  57.     }
  58. }
复制代码
json序列化总结
6.Protobuf

Protobuf是谷歌推出的,是一种语言无关、平台无关、可扩展的序列化结构数据的方法,它可用于通信协议、数据存储等。序列化后体积小,一般用于对传输性能有较高要求的系统。前期需要额外配置环境变量,学习他的语法也是需要时间。
但是要使用Protobuf会相对来说麻烦一些,因为他有自己的语法,有自己的编译器,如果需要用到的话必须要去投入成本在这个技术的学习中
Protobuf有个缺点就是传输的每一个类的结构都要生成相对应的proto文件,如果某个类发生修改,还要重新生成该类对应的proto文件。另外,Protobuf对象冗余,字段很多,生成的类较大,占用空间。维护成本较高。
  1. // 声明语法,不显示声明默认是2.0的语法。
  2. syntax = "proto3";
  3. // 指定模板类的包路径
  4. option java_package = "com.test.basic.java.serialize.proto.dto";
  5. // 指定模板类的名称,名称必须是有实际业务意义的
  6. option java_outer_classname = "UserProto";
  7. // 定义用户对象
  8. message User {
  9.   // 名字
  10.   string name = 1;
  11.   // 年龄
  12.   int32 age = 2;
  13. }
  14. // 声明语法,不显示声明默认是2.0的语法。
  15. syntax = "proto3";
  16. // 指定模板类的包路径
  17. option java_package = "com\nateshao\source\code\serializable\protobuf_Serializable\User.proto";
  18. // 指定模板类的名称,名称必须是有实际业务意义的
  19. option java_outer_classname = "UserProto";
  20. // 定义用户对象
  21. message User {
  22.   // 名字
  23.   string name = 1;
  24.   // 年龄
  25.   int32 age = 2;
  26. }
复制代码
7.MessagePack(推荐)



不好的地方:
上手

通过配置msgpack的maven依赖
  1. <dependency>
  2.     <groupId>org.msgpack</groupId>
  3.     <artifactId>msgpack</artifactId>
  4.     <version>0.6.12</version>
  5. </dependency>
复制代码
封装MsgPackTemplate抽象类,对原有msgpack进行二次加工,比如int,Short,Byte,BigDecimal进行read和write处理
MsgPackTemplate.java
  1. package com.nateshao.source.code.serializable.msgpack_Serializable;
  2. import java.io.IOException;
  3. import java.math.BigDecimal;
  4. import java.util.Date;
  5. import org.msgpack.packer.Packer;
  6. import org.msgpack.template.CharacterTemplate;
  7. import org.msgpack.template.Template;
  8. import org.msgpack.unpacker.Unpacker;
  9. /**
  10. * @date Created by 邵桐杰 on 2023/2/25 23:11
  11. * @微信公众号 千羽的编程时光
  12. * @博客 https://nateshao.gitlab.io
  13. * @GitHub https://github.com/nateshao
  14. * Description:
  15. */
  16. public abstract class MsgPackTemplate <T> implements Template<T> {
  17.     public void write(Packer pk, T v) throws IOException {
  18.         write(pk, v, false);
  19.     }
  20.     public T read(Unpacker u, T to) throws IOException {
  21.         return read(u, to, false);
  22.     }
  23.     public BigDecimal readBigDecimal(Unpacker u) throws IOException {
  24.         if (u.trySkipNil()) {
  25.             return null;
  26.         }
  27.         String temp = u.readString();
  28.         if (temp != null) {
  29.             return new BigDecimal(temp);
  30.         }
  31.         return null;
  32.     }
  33.     public Date readDate(Unpacker u) throws IOException {
  34.         if (u.trySkipNil()) {
  35.             return null;
  36.         }
  37.         long temp = u.readLong();
  38.         if (temp > 0) {
  39.             return new Date(temp);
  40.         }
  41.         return null;
  42.     }
  43.     public String readString(Unpacker u) throws IOException {
  44.         if (u.trySkipNil()) {
  45.             return null;
  46.         }
  47.         return u.readString();
  48.     }
  49.     public Integer readInteger(Unpacker u) throws IOException {
  50.         if (u.trySkipNil()) {
  51.             return null;
  52.         }
  53.         return u.readInt();
  54.     }
  55.     public Byte readByte(Unpacker u) throws IOException {
  56.         if (u.trySkipNil()) {
  57.             return null;
  58.         }
  59.         return u.readByte();
  60.     }
  61.     public Short readShort(Unpacker u) throws IOException {
  62.         if (u.trySkipNil()) {
  63.             return null;
  64.         }
  65.         return u.readShort();
  66.     }
  67.     public Float readFloat(Unpacker u) throws IOException {
  68.         if (u.trySkipNil()) {
  69.             return null;
  70.         }
  71.         return u.readFloat();
  72.     }
  73.     public Double readDouble(Unpacker u) throws IOException {
  74.         if (u.trySkipNil()) {
  75.             return null;
  76.         }
  77.         return u.readDouble();
  78.     }
  79.     public Character readCharacter(Unpacker u) throws IOException {
  80.         if (u.trySkipNil()) {
  81.             return null;
  82.         }
  83.         return u.read(CharacterTemplate.getInstance());
  84.     }
  85.     public Long readLong(Unpacker u) throws IOException {
  86.         if (u.trySkipNil()) {
  87.             return null;
  88.         }
  89.         return u.readLong();
  90.     }
  91.     public Boolean readBoolean(Unpacker u) throws IOException {
  92.         if (u.trySkipNil()) {
  93.             return null;
  94.         }
  95.         return u.readBoolean();
  96.     }
  97.     public void writeBigDecimal(Packer pk, BigDecimal v) throws IOException {
  98.         if (v == null) {
  99.             pk.writeNil();
  100.         } else {
  101.             pk.write(v.toString());
  102.         }
  103.     }
  104.     public void writeDate(Packer pk, Date v) throws IOException {
  105.         if (v == null) {
  106.             pk.writeNil();
  107.         } else {
  108.             pk.write(v.getTime());
  109.         }
  110.     }
  111.     public void writeString(Packer pk, String v) throws IOException {
  112.         if (v == null) {
  113.             pk.writeNil();
  114.         } else {
  115.             pk.write(v);
  116.         }
  117.     }
  118.     public void writeInt(Packer pk, int v) throws IOException {
  119.         pk.write(v);
  120.     }
  121.     public void writeInteger(Packer pk, Integer v) throws IOException {
  122.         if (v == null) {
  123.             pk.writeNil();
  124.         } else {
  125.             pk.write(v.intValue());
  126.         }
  127.     }
  128.     public void writeByte(Packer pk, Byte v) throws IOException {
  129.         if (v == null) {
  130.             pk.writeNil();
  131.         } else {
  132.             pk.write(v.byteValue());
  133.         }
  134.     }
  135.     public void writeShort(Packer pk, Short v) throws IOException {
  136.         if (v == null) {
  137.             pk.writeNil();
  138.         } else {
  139.             pk.write(v.shortValue());
  140.         }
  141.     }
  142.     public void writeFloat(Packer pk, Float v) throws IOException {
  143.         if (v == null) {
  144.             pk.writeNil();
  145.         } else {
  146.             pk.write(v.floatValue());
  147.         }
  148.     }
  149.     public void writeDouble(Packer pk, Double v) throws IOException {
  150.         if (v == null) {
  151.             pk.writeNil();
  152.         } else {
  153.             pk.write(v.doubleValue());
  154.         }
  155.     }
  156.     public void writeCharacter(Packer pk, Character v) throws IOException {
  157.         if (v == null) {
  158.             pk.writeNil();
  159.         } else {
  160.             pk.write(v.charValue());
  161.         }
  162.     }
  163.     public void writeLong(Packer pk, Long v) throws IOException {
  164.         if (v == null) {
  165.             pk.writeNil();
  166.         } else {
  167.             pk.write(v.longValue());
  168.         }
  169.     }
  170.     public void writeLong(Packer pk, long v) throws IOException {
  171.         pk.write(v);
  172.     }
  173.     public void writeBoolean(Packer pk, Boolean v) throws IOException {
  174.         if (v == null) {
  175.             pk.writeNil();
  176.         } else {
  177.             pk.write(v.booleanValue());
  178.         }
  179.     }
  180. }
复制代码
然后针对实体类进行读和写的序列化与反序列化操作。
User.java
  1. package com.nateshao.source.code.serializable.msgpack_Serializable;
  2. @Data
  3. @ToString
  4. @AllArgsConstructor
  5. @NoArgsConstructor
  6. public class User {
  7.     private static final long serialVersionUID = 4719921525393585541L;
  8.     protected String id;
  9.     private String userID;
  10.     private String type;
  11.     private BigDecimal rate;
  12.     private Date createTime;
  13.     private Date UpdateTime;
  14. }
复制代码
UserTemplate.java
  1. package com.nateshao.source.code.serializable.msgpack_Serializable;
  2. import org.msgpack.packer.Packer;
  3. import org.msgpack.unpacker.Unpacker;
  4. import java.io.IOException;
  5. /**
  6. * protected String id;
  7. * private String userID;
  8. * private String type;
  9. * private BigDecimal rate;
  10. * private Date createTime;
  11. * private Date UpdateTime;
  12. */
  13. public class UserTemplate extends MsgPackTemplate<User> {
  14.     public void write(Packer packer, User user, boolean b) throws IOException {
  15.         if (user == null) {
  16.             packer.write(user);
  17.             return;
  18.         }
  19.         // 字段保持一致
  20.         writeString(packer, user.id);
  21.         writeString(packer, user.getUserID());
  22.         writeString(packer, user.getType());
  23.         writeBigDecimal(packer, user.getRate());
  24.         writeDate(packer, user.getCreateTime());
  25.         writeDate(packer, user.getUpdateTime());
  26.     }
  27.     public User read(Unpacker unpacker, User user, boolean req) throws IOException {
  28.         if (!req && unpacker.trySkipNil()) {
  29.             return null;
  30.         }
  31.         if (unpacker == null) return new User();
  32.         user.setId(readString(unpacker));
  33.         user.setUserID(readString(unpacker));
  34.         user.setType(readString(unpacker));
  35.         user.setRate(readBigDecimal(unpacker));
  36.         user.setCreateTime(readDate(unpacker));
  37.         user.setUpdateTime(readDate(unpacker));
  38.         return user;
  39.     }
  40. }
复制代码
参考文献:
作者:京东零售 邵桐杰
来源:京东云开发者社区

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!




欢迎光临 IT评测·应用市场-qidao123.com (https://dis.qidao123.com/) Powered by Discuz! X3.4