二进制序列化

在计算机世界,万物皆01二进制,包括各种各样的文件格式和网络协议,二进制格式最为常见!NewLife.Core 内置了完整的二进制序列化框架 Binary,经过十多年洗礼,发展到了第三代支持Handler处理器扩展。Binary的同类框架有 Protobuf、Thrift、MessagePack。

Nuget包:NewLife.Core

源码地址:https://github.com/NewLifeX/X/tree/master/NewLife.Core/Serialization/Binary

主要特性

Binary主要功能特性:

  1. 体积极小。Binary是Schemaless架构,不包含字段名和序号,用最少的字节去保存数据

  2. 压缩整数。大多数时候Int32字段保存的数字很小,采用七位压缩编码整数保存可以减少体积

  3. 格式简单。尽管Binary只有.NET版,但其格式非常简单,可以很容易在其它语言上实现

  4. 支持性很广。Binary设计初衷,就是用于实现各种已知文件格式和通信协议,例如ZipFile

  5. 支持动态特性。可根据某些字段值,生成不同消息类型,例如MQTT和DNS协议

  6. 可读性较差。二进制格式且没有Schema,可读性较差

  7. 无版本支持。需要读写双方约定好多版本格式的兼容

Binary设计理念,就是用最小的体积去保存数据,且能够灵活实现各种文件格式和通信协议的序列化。

直接序列化对象,在没有使用额外压缩算法的条件下,Binary几乎是结果体积最小的序列化框架。

快速用法

想要序列化一个对象,或者反序列化一个数据流到对象,最直接的想法就是这样

// 快速读取
public static T FastRead<T>(Stream stream, Boolean encodeInt = true);
// 快速写入
public static Packet FastWrite(Object value, Boolean encodeInt = true);
public static void FastWrite(Object value, Stream stream, Boolean encodeInt = true);

Binary.FastWrite 可以直接把一个对象序列化为数据包Packet,可以理解为字节数组Byte[]的包装。

Binary.FastRead 从数据流中反序列化得到目标类型的对象,这里必须指定目标类型,否则Binary不知道应该如何解析。

例子

[Fact]
public void Fast()
{var model = new MyModel { Code = 1234, Name = "Stone" };var pk = Binary.FastWrite(model);Assert.Equal(8, pk.Total);Assert.Equal("D2090553746F6E65", pk.ToHex());Assert.Equal("0gkFU3RvbmU=", pk.ToArray().ToBase64());var model2 = Binary.FastRead<MyModel>(pk.GetStream());Assert.Equal(model.Code, model2.Code);Assert.Equal(model.Name, model2.Name);var ms = new MemoryStream();Binary.FastWrite(model, ms);Assert.Equal("D2090553746F6E65", ms.ToArray().ToHex());
}
private class MyModel
{public Int32 Code { get; set; }public String Name { get; set; }
}

序列化带有一个整型和一个字符串的对象,结果只有8个字节!

Packet用法可参考

此处为语雀文档,点击链接查看:https://www.yuque.com/go/doc/31527106

标准读写

Binary主要成员

/// <summary>使用7位编码整数。默认false不使用</summary>
public Boolean EncodeInt { get; set; }
/// <summary>小端字节序。默认false大端</summary>
public Boolean IsLittleEndian { get; set; }
/// <summary>使用指定大小的FieldSizeAttribute特性,默认false</summary>
public Boolean UseFieldSize { get; set; }
/// <summary>使用对象引用,默认true</summary>
public Boolean UseRef { get; set; } = true;
/// <summary>大小宽度。可选0/1/2/4,默认0表示压缩编码整数</summary>
public Int32 SizeWidth { get; set; }
/// <summary>要忽略的成员</summary>
public ICollection<String> IgnoreMembers { get; set; }
/// <summary>处理器列表</summary>
public IList<IBinaryHandler> Handlers { get; private set; }
/// <summary>数据流。默认实例化一个内存数据流</summary>
public virtual Stream Stream { get; set; }
/// <summary>主对象</summary>
public Stack<Object> Hosts { get; private set; }
/// <summary>成员</summary>
public MemberInfo Member { get; set; }
/// <summary>字符串编码,默认utf-8</summary>
public Encoding Encoding { get; set; }
/// <summary>序列化属性而不是字段。默认true</summary>
public Boolean UseProperty { get; set; }
// 处理器
public Binary AddHandler(IBinaryHandler handler);
public Binary AddHandler<THandler>(Int32 priority = 0);
public T GetHandler<T>();
// 写入
public virtual Boolean Write(Object value, Type type = null);
// 读取
public virtual Object Read(Type type);
public T Read<T>();
public virtual Boolean TryRead(Type type, ref Object value);

Stream 最为重要,代表序列化和反序列化的数据流,默认实例化一个内存流。

EncodeInt 指定使用压缩编码整数,效果非常明显!

IsLittleEndian 部分协议使用大端字节序。

UseFieldSize 部分协议的长度位和数据区并没有挨在一起,需要借助FieldSizeAttribute特性。例如ZipEntry中有这么一段:

/// <summary>文件名长度</summary>
private readonly UInt16 FileNameLength;
/// <summary>扩展数据长度</summary>
private readonly UInt16 ExtraFieldLength;
// ZipDirEntry成员
/// <summary>注释长度</summary>
private readonly UInt16 CommentLength;
// ZipDirEntry成员
/// <summary>分卷号。</summary>
public UInt16 DiskNumber;
// ZipDirEntry成员
/// <summary>内部文件属性</summary>
public UInt16 InternalFileAttrs;
// ZipDirEntry成员
/// <summary>扩展文件属性</summary>
public UInt32 ExternalFileAttrs;
// ZipDirEntry成员
/// <summary>文件头相对位移</summary>
public UInt32 RelativeOffsetOfLocalHeader;
/// <summary>文件名,如果是目录,则以/结束</summary>
[FieldSize("FileNameLength")]
public String FileName;
/// <summary>扩展字段</summary>
[FieldSize("ExtraFieldLength")]
public Byte[] ExtraField;
// ZipDirEntry成员
/// <summary>注释</summary>
[FieldSize("CommentLength")]
public String Comment;

IgnoreMembers 指定某些成员不参与序列化,支持动态指定。例如ZipFile的目录实体和文件实体,需要序列化的字段有所不同。

Encoding 指定序列化字符串时使用的文本编码。

设置好各种参数后,就可以Write/Read来序列化或反序列化对象了。安全起见,建议每个Binary只用一次,重复使用可能有意想不到的后果。

自定义扩展

Binary设计时使用Handler处理器架构,Write/Read内部实际上是逐个遍历Handler,直到找到能够处理的Handler为止。因此Handler也有优先级,其中基础数据类型BinaryGeneral处理器优先级最高。

BinaryGeneral 负责处理数字、布尔、时间日期、字符串等等基础数据类型。

BinaryNormal 负责处理字节数组、Guid、Packet等常见类型。

BinaryList 负责处理数组和列表。

BinaryDictionary 负责处理字典。

BinaryComposite 负责处理复杂对象,反射各成员,递归序列化。该处理器优先级最低。

来看看怎么样自定义一个处理器,以颜色处理器为例:

/// <summary>颜色处理器。</summary>
public class BinaryColor : BinaryHandlerBase
{/// <summary>实例化</summary>public BinaryColor(){Priority = 0x50;}/// <summary>写入对象</summary>/// <param name="value">目标对象</param>/// <param name="type">类型</param>/// <returns></returns>public override Boolean Write(Object value, Type type){if (type != typeof(Color)) return false;var color = (Color)value;WriteLog("WriteColor {0}", color);Host.Write(color.A);Host.Write(color.R);Host.Write(color.G);Host.Write(color.B);return true;}/// <summary>尝试读取指定类型对象</summary>/// <param name="type"></param>/// <param name="value"></param>/// <returns></returns>public override Boolean TryRead(Type type, ref Object value){if (type != typeof(Color)) return false;var a = Host.ReadByte();var r = Host.ReadByte();var g = Host.ReadByte();var b = Host.ReadByte();var color = Color.FromArgb(a, r, g, b);WriteLog("ReadColor {0}", color);value = color;return true;}
}

最后只需要挂载到Binary上即可序列化和反序列化带有Color类型的成员

var bn = new Binary();
bn.AddHandler<BinaryColor>();

总结

.NET内部自带二进制序列化BinaryFormatter,它会带上大量额外信息,导致体积很大,基本上很少用到。

Binary设计的初衷是序列化各种文件格式和通信协议,因此并没有过多考虑作为RPC通信格式。实际上NewLife组件自己的RPC框架ApiServer并没有使用Binary,而是选择了兼容性比较好的Json。

在中通的100亿Redis大数据中,尽管是二进制kv数据,同样没有用到Binary。因为它需要对字节数据进行极致控制,并且需要做多版本兼容。因此它实际上是直接读写二进制数据流,然后借用了Binary的一些辅助方法。

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

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

相关文章

python搭建项目结构_Django搭建项目实战与避坑细节详解

Django 开发项目是很快的&#xff0c;有多快&#xff1f;看完本篇文章&#xff0c;你就知道了。安装 Django前提条件&#xff1a;已安装 Python。Django 使用 pip 命令直接就可以安装&#xff1a;pip install django如果安装失败&#xff0c;很可能是因为网络连接超时了&#x…

matlab基于ssd的角点匹配_基于关键点的目标检测

0 1前言&#xff1a;基于锚点的目标检测方法在基于关键点(key points)的目标检测方法出现之前&#xff0c;主流目标检测方法一般先设置一些预先定义好的 锚点 (anchor boxes)。 作为预测物体框的参考&#xff0c;神经网络只需要预测实际的物体框相对于这些锚点的偏移。 这样模型…

python中summary_python summary_study.py

#!/usr/bin/env python#-*- coding:utf-8 -*-import os,sys,time,shutil,refrom _ast import Numfrom django.db.models.sql.datastructures import Join---------------------------此脚本用于之前学习内容的回顾装饰器还需要多看&#xff0c;目前还是不是很清楚类的继承prope…

NLog整合Exceptionless

前言在实际的.Net Core相关项目开发中&#xff0c;很多人都会把NLog作为日志框架的首选&#xff0c;主要是源于它的强大和它的扩展性。同时很多时候我们需要集中式的采集日志&#xff0c;这时候仅仅使用NLog是不够的&#xff0c;NLog主要是负责代码中日志的落地&#xff0c;也就…

40个只有程序员才看得懂的段子

1. 一程序员去面试&#xff0c;面试官问&#xff1a;“你毕业才两年&#xff0c;这三年工作经验是怎么来的&#xff1f;&#xff01;”程序员答&#xff1a;“加班。”2. 某程序员对书法十分感兴趣&#xff0c;退休后决定在这方面有所建树。于是花重金购买了上等的文房四宝。一…

bra型手机链

左看看&#xff0c;右瞧瞧&#xff0c;真不敢相信这个居然是手机链&#xff1f;带上它出门&#xff0c;回头率一定很高哦&#xff01;就是有点贵&#xff0c;70元&#xff01;转载于:https://blog.51cto.com/laizhngn5376/180850

MySQL8的inodb参数设置_MySQL8.0自适应参数innodb_dedicated_server

MySQL8.0有了一个新参数又叫自适应参数 innodb_dedicated_server将innodb_dedicated_server开启的时候&#xff0c;它可以自动的调整下面这四个参数的值&#xff1a;innodb_buffer_pool_size 总内存大小innodb_log_file_size redo文件大小innodb_log_files_in_group redo文件数…

让 gRPC 提供 REST 服务

让 gRPC 提供 REST 服务IntrogRPC 是一个高性能、开源和通用的 RPC 框架&#xff0c;面向移动和 HTTP/2 设计。gRPC 基于 HTTP/2 标准设计&#xff0c;带来诸如双向流、流控、头部压缩、单 TCP 连接上的多复用请求等特。这些特性使得其在移动设备上表现更好&#xff0c;更省电…

python提取cad坐标到excel_使用python来操作autocad,并且将坐标点转换成cad可见对象...

由于工作需要&#xff0c;在项目中遇到一个棘手的问题&#xff0c;如何将(mssql)数据库中的BLOB文件转成cad可见图形(可能每个项目需求不一样&#xff0c;解决方式不同)第一步 . 需要转换的图形类型第二步 . 那我们先查询这个字段第三步 试试将这个写入一个文本中 看看是那种图…

10张图看懂瞎忙和高效的区别

时间是最公平的&#xff0c;每个人一天都是24小时&#xff0c;一年都是365天。但是&#xff0c;不同的人的产出却是天差地别。人和人的差距为什么这么大&#xff1f;而且这种差距&#xff0c;并不是家庭背景、权利财富或天赋带来的&#xff0c;仅仅是我们对时间的掌控。正好看到…

pc模式 华为mate30_华为mate30与电脑连不上怎么回事

大家好&#xff0c;我是时间财富网智能客服时间君&#xff0c;上述问题将由我为大家进行解答。系统版本为EMUI 10.1&#xff0c;华为mate30与电脑连不上的原因&#xff1a;1、可能是USB线连接不正常。建议更换数据线试试。2、确认电脑上的手机USB 驱动已经安装成功&#xff0c;…

ASP.NET Core 中的配置

背景ASP.NET Core 提供了一个灵活可扩展,基于键值的配置系统. 但是配置系统独立于ASP.NET Core是Microsoft.Extensions 类库的部分. 它可以用于任何类型的应用程序。1、以键-值对的形式读取配置appsettings.json 文件&#xff1a;{"Position": {"Title": &…

Canvas的save和restore

在创建新的控件或修改现有的控件时&#xff0c;我们都会涉及到重写控件或View的onDraw方法。 onDraw方法会传入一个Canvas对象&#xff0c;它是你用来绘制控件视觉界面的画布。 在onDraw方法里&#xff0c;我们经常会看到调用save和restore方法&#xff0c;它们到底是干什么用的…

vs code python 插件_工具篇-vscode效率提升插件

工欲善其事必先利其器&#xff0c;开发前先把所以提升效率的利器搭好会让今后慢慢的编程长路舒服很多&#xff0c;我本来一直用pycharm&#xff0c;后来发现vscode貌似确实会好很多。。。就慢慢转过来了&#xff0c;下面介绍一些我在机器学习编程时经常会用到的一些插件。1. au…

鉴别一个人是否 js 入门的标准竟然是?!

不知不觉跳入前端「大坑」也已经有大半年了&#xff0c;学到了很多知识。为了让知识更好地沉淀&#xff0c;我打算写一系列的知识总结&#xff0c;希望能在回顾知识的同时也能帮到别的同学。忘记在哪里看到过&#xff0c;有人说鉴别一个人是否 js 入门的标准就是看他有没有理解…

面向对象编程设计模式--简单工厂模式讲解(历史上最简单明白的例子)

工作之余&#xff0c;在看资料过程中发现一个极易理解的简单工厂模式的例子&#xff0c;自己亲自试练一番,感觉对这个设计模式不熟悉的朋友&#xff0c;一看马上就知道是什么回事了。 简单工厂模式根据提供给它的数据&#xff0c;返回几个可能类中的一个类的实例。通常它返的类…

.NET 6 Preview 1 开箱,带你体验新版本

最近 .NET 6 Preview 1 发布了&#xff0c;.NET 统一是此版本的核心。大家可以读一下原文博客&#xff1a;https://devblogs.microsoft.com/dotnet/announcing-net-6-preview-1/.NET 6.0 SDK 和 Runtime 下载地址&#xff1a;https://dotnet.microsoft.com/download/dotnet/6.0…

redis 清空缓存_「镜头回放」简直了!spring中清除redis缓存导致应用挂死

异常场景springWeb应用一直运行正常&#xff0c;同事最近反应&#xff0c;每次版本更新完毕&#xff0c;刷新缓存&#xff0c;就会导致应用挂死。只有重启redis应用才恢复正常。项目概况springWeb项目&#xff0c;常用配置表做了redis缓存&#xff0c;配置表中只有少量数据&…

25岁社招进阿里,从电商到有赞新零售,他仅1年就打开了马云一直想做的新领域!

最近关于「新零售」的声音此起彼伏&#xff1a;阿里巨资收购高鑫零售&#xff0c;腾讯确认入股永辉超市……自2016年10月马云第一次提出了「新零售」概念之后&#xff0c;各巨头跑马圈地&#xff0c;线下成为了必争之地&#xff0c;新零售的蓝海才刚刚打开。而李星&#xff0c;…

优美的测试代码 - 行为驱动开发(BDD)

可理解的代码非常重要&#xff0c;测试代码也是如此。在我看来&#xff0c;优秀的测试代码&#xff0c;必须做到一个重要的事情就是保持测试逻辑的清晰。一个完整的测试案例通常包括三个部分&#xff1a;1. SetUp2. Exercise3. Verifiy4. TearDown一 个测试案例如果能清晰的区分…