Netty笔记13:序列化

发布于:2025-03-06 ⋅ 阅读:(23) ⋅ 点赞:(0)

Netty笔记1:线程模型

Netty笔记2:零拷贝

Netty笔记3:NIO编程

Netty笔记4:Epoll

Netty笔记5:Netty开发实例

Netty笔记6:Netty组件

Netty笔记7:ChannelPromise通知处理

Netty笔记8:ByteBuf使用介绍

Netty笔记9:粘包半包

Netty笔记10:LengthFieldBasedFrameDecoder

Netty笔记11:编解码器

Netty笔记12:模拟Web服务器

Netty笔记13:序列化


这里我只是搞了 MessagePackJackson这两种,他们使用和性能上算是不错的。

Java中实现序列化只需要implements Serializable ,但是这种方式限定了Java平台下,并且这种方式的性能也是很差的,不但内存占用高,消耗时间有长,具体的可以看这篇文章:几种Java常用序列化框架的选型与对比-阿里云开发者社区 (aliyun.com)

MessagePack(多语言支持)

MessagePack is a binary serialization format. If you need a fast and compact alternative of JSON, MessagePack is your friend. For example, a small integer can be encoded in a single byte, and short strings only need a single byte prefix + the original byte array. MessagePack implementation is already available in various languages (See also the list in http://msgpack.org) and works as a universal data format.

Message Pack specification: https://github.com/msgpack/msgpack/blob/master/spec.md
MessagePack v7 (or later) is a faster implementation of the previous version v06, and supports all of the message pack types, including extension format.

--->

MessagePack 是一种二进制序列化格式。如果你需要一个比 JSON 更快、更紧凑的替代方案,MessagePack 就是你的选择。例如,一个小整数可以仅用一个字节编码,短字符串只需要一个字节前缀加上原始字节数组。MessagePack 的实现已经存在于多种语言中(也可以参见列表),并且作为一个通用的数据格式工作。

MessagePack 规范:
MessagePack v7(或更高版本)是之前版本 v06 的更快实现,并支持所有 MessagePack 类型,包括扩展格式。

优点:

  1. 高效:节省空间;
  2. 跨语言:MessagePack支持多种语言;
  3. 易于使用:序列化和反序列化调用API就行,不像有些框架需要些配置,编译等复杂操作;

缺点:

  1. 复杂数据类型支持不完善;

引入依赖,限制最新版本是0.9.8

<dependency>
    <groupId>org.msgpack</groupId>
    <artifactId>msgpack-core</artifactId>
    <version>0.9.8</version>
</dependency>
  1. 发送端用MessagePack序列化

    public class MsgEncoder extends MessageToByteEncoder<UserInfo> {
        @Override
        protected void encode(ChannelHandlerContext ctx, UserInfo msg, ByteBuf out) throws Exception {
    
            ByteArrayOutputStream bos = new ByteArrayOutputStream();
            try (MessagePacker mp = MessagePack.newDefaultPacker(bos);) {
                mp.packMapHeader(3);
    
                mp.packString("name").packString(msg.getName());;
                mp.packString("id").packInt(msg.getId());
                mp.packString("data").packString(msg.getData());
                // 设置完属性,记得调用这个方法将数据刷到bos
                mp.flush();
    
                byte[] bytes = bos.toByteArray();
    
                out.writeBytes(bytes);
            }
        }
    }
    
    1. 接收端用户MessagePack反序列化

      public class MsgDecoder extends ByteToMessageDecoder {
          @Override
          protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {
      
              int length = in.readableBytes();
              byte[] bytes = new byte[length];
              in.readBytes(bytes);;
      
              try (MessageUnpacker mp = MessagePack.newDefaultUnpacker(bytes)) {
                  ImmutableValue value = mp.unpackValue();
                  if (!value.isMapValue()) {
                      throw new Exception("数据格式不对");
                  }
                  Map<Value, Value> map = value.asMapValue().map();
      
                  String name = map.get(ValueFactory.newString("name")).asStringValue().asString();
                  int id = map.get(ValueFactory.newString("id")).asIntegerValue().asInt();
                  String data = map.get(ValueFactory.newString("data")).asStringValue().asString();
      
                  UserInfo info = new UserInfo();
                  info.setName(name);
                  info.setId(id);
                  info.setData(data);
      
                  System.out.printf("读取到数据:name:%s, id:%d, data:%s%n", name, id, data);
                  out.add(info);
              }
          }
      }
      

jackson

jackson有官方提供的示例,建议去看看:msgpack-java/msgpack-jackson at main · msgpack/msgpack-java · GitHub

<dependency>
  <groupId>org.msgpack</groupId>
  <artifactId>jackson-dataformat-msgpack</artifactId>
  <version>0.9.8</version>
</dependency>

需要注意:ObjectMapper是线程安全的,这里示例为了读者清晰的思路写在一起了,实际开发中应该是全局配置或特殊配置的。

  1. 发送到编码器
public class MsgJacksonEncoder extends MessageToByteEncoder<UserInfo> {
    @Override
    protected void encode(ChannelHandlerContext ctx, UserInfo msg, ByteBuf out) throws Exception {
        // 方法一
        ObjectMapper objectMapper = new ObjectMapper(new MessagePackFactory());
        byte[] bytes = objectMapper.writeValueAsBytes(msg);
        out.writeBytes(bytes);
        // 方法二
//        ObjectMapper objectMapper2 = new MessagePackMapper();
//        byte[] bytes2 = objectMapper2.writeValueAsBytes(msg);
//        out.writeBytes(bytes2);
    }
}
  1. 接收端解码器
public class MsgJacksonDecoder extends ByteToMessageDecoder {
    @Override
    protected void decode(ChannelHandlerContext ctx, ByteBuf in, List<Object> out) throws Exception {

        int length = in.readableBytes();
        byte[] bytes = new byte[length];
        in.readBytes(bytes);;

        // 方法一
        ObjectMapper objectMapper = new ObjectMapper(new MessagePackFactory());
//        UserInfo info = objectMapper.readValue(bytes, UserInfo.class);
//        System.out.println("读取到数据:" + JSON.toJSONString(info));
        // 方法二
//        ObjectMapper objectMapper2 = new MessagePackMapper();
//        UserInfo info2 = objectMapper2.readValue(bytes, UserInfo.class);
//        System.out.println("读取到数据:" + JSON.toJSONString(info2));

        // 类型转换的另一种方式
        TypeReference<UserInfo> typeReference = new TypeReference<UserInfo>(){};
        UserInfo info3 = objectMapper.readValue(bytes, typeReference);
        System.out.println("读取到数据:" + JSON.toJSONString(info3));
    }
}

需要注意:ObjectMapper是线程安全的,这里示例为了读者清晰的思路写在一起了,实际开发中应该是全局配置或特殊配置的。