大家期待已久的(下)示例整合终于来了,但看标题为什么是番外呢?其实在(中)篇中提到示例整合,需要引入新知识,这也是必备的前置条件。那么这一篇重点就是介绍相关的新知识,既协议部分。
废话不多说,咱们直接进入正题:本文主要介绍Protobuf协议、自定义二进制协议,如果大家能够很好的掌握,举一反三、灵活运用,相信在平时工作中关于协议相关的工作都能迎刃而解(设计思想、解决方案、处理流程都是极其相似的)。
一个好的协议有两个标准:
1.生成的传输数据要少,即数据压缩比要高。这样可以减少网络开销。
2.传输数据和业务对象之间的转换速度要快。
一、protobuf协议的定义
Protobuf,全称为Protocol Buffers,是Google开发的一种轻量级的数据交换格式。
它是一种语言无关、平台无关的序列化机制,适用于数据存储和通信协议的定义。
Protobuf的主要目标是提供高效的数据序列化和反序列化机制,使得数据在网络传输和存储时更加高效和可靠。
二、protobuf的优缺点
2.1、优点
简洁的结构定义语言:
Protobuf使用一种简洁的结构定义语言来定义数据结构和消息格式。
这种语言具有类似于C语言的语法,使用起来非常直观和简单。通过定义消息结构,可以指定每个字段的名称、类型和顺序。
高效的序列化和反序列化:
相比于其他序列化机制(如XML和JSON),Protobuf具有更高的性能和更小的数据体积。
Protobuf使用二进制格式进行数据的序列化和反序列化,这种格式更加紧凑,占用的存储空间更小,传输速度更快。
可扩展性和兼容性:
Protobuf支持数据结构的向前和向后兼容。当数据结构发生变化时,可以向旧的数据结构中添加新的字段,而不会影响已有的数据。
这种可扩展性使得系统可以在不中断服务的情况下进行升级和演化。
多语言支持:
Protobuf支持多种编程语言,并提供了对应的代码生成工具。
通过使用这些工具,可以根据消息结构定义自动生成各种编程语言的代码,简化了开发过程。
2.2、缺点
可读性差:
Protobuf使用二进制格式进行数据序列化,这使得数据在传输和存储时更加紧凑和高效,但也导致了数据的可读性较差。
与人类可读的文本格式(如JSON和XML)相比,Protobuf的二进制格式很难直接查看和理解。
不支持动态修改:
一旦消息结构定义后,就很难对其进行动态修改。如果需要添加、删除或修改字段,必须修改消息结构的定义,并使用代码生成工具重新生成代码。
这对于一些需要频繁变更数据结构的场景可能不够灵活。
版本兼容性:
Protobuf在消息结构发生变化时,支持向前和向后兼容。但是,当消息结构变化较大时,可能会出现一些兼容性问题。
比如,删除或重命名字段可能会导致旧版本的代码无法正确处理新版本的数据。
学习成本高:
相比于其他一些数据交换格式,如JSON和XML,学习Protobuf的使用和定义消息结构的语法可能需要更多的时间和精力。
类似的数据交换格式包括:
JSON:JSON是一种轻量级的数据交换格式,具有良好的可读性和广泛的支持。与Protobuf相比,JSON的主要优点是易于阅读和调试,但它的数据体积较大,序列化和反序列化的性能较低。
XML:XML是一种可扩展的标记语言,用于描述数据结构。与Protobuf相比,XML的主要优点是广泛支持和易于理解,但它的数据体积较大,序列化和反序列化的性能较低。
MessagePack:
MessagePack是一种高效的二进制数据交换格式,具有较小的数据体积和较快的序列化和反序列化性能。
与Protobuf相比,MessagePack的主要优点是更容易阅读和理解,但它的可扩展性和兼容性较弱。
选择使用哪种数据交换格式需要根据具体的应用场景和需求来决定。如果注重性能和数据体积,
可以选择Protobuf或MessagePack;如果注重可读性和易用性,可以选择JSON或XML。
Protobuf 协议制定 (因整合 DEMO 演示用,示例协议非常简单)
一、协议的制定
无论是请求还是响应,报文都由一个通用报文头和实际数据组成。报文头在前,数据在后。
1.报文头:由协议头,消息类型,响应消息,数据单元长度组成,共5个字节:
协议头(1byte)、消息类型(1byte)、响应消息(1byte)、数据单元长度(1byte)
2.数据单元:由数据单元长度指定。
二、代码示例(示例中代码已经过编译生成对应Java对象,可直接使用):
syntax = "proto3";
option java_package = "com.kevin.netty.protocol.protobuf";
option go_package = "com.kevin.netty/protocol/protobuf";
option java_outer_classname = "BasicMessage";
package BasicMessage;
message Basic {
string headString = 1; // 协议头,无实际意义,固定头信息$$ ,占两个字节
RequestType requestType = 2; // 消息类型,占一个字节
ResponseType responseType = 3; // 响应消息,占一个字节
int32 payloadLength = 4; // 数据单元长度,占一个字节
string payload = 5; // 数据单元,按照数据单元长度
}
enum RequestType {
UNKNOWN_REQUEST = 0; // 未知类型
NOTICE = 1; // 通知消息
}
enum ResponseType{
UNKNOWN_RESPONSE = 0; // 未知类型
SUCCESS = 1; // 通知消息
FAILED = 2;
}
三、代码编译(请自行百度编译方法,编译器示例中已提供,在bin目录下选择相应编译器)
二进制协议制定 (因整合 DEMO 演示用,示例协议非常简单)
一、协议的制定
无论是请求还是响应,报文都由一个通用报文头和实际数据组成。报文头在前,数据在后。
1.报文头:由协议头,消息类型,响应消息,数据单元长度组成,共5个字节:
协议头(1byte)、消息类型(1byte)、响应消息(1byte)、数据单元长度(1byte)
2.数据单元:由数据单元长度指定。
二、代码示例:
@Getter
@Setter
@ToString
@Accessors(chain = true)
public class BasicMessage implements Serializable {
@Serial
private static final long serialVersionUID = 9201755938626937722L;
/**
* 协议头,无实际意义,固定头信息$$ ,占两个字节
*/
private String headSting;
/**
* 消息类型,占一个字节
*/
private RequestType requestType;
/**
* 响应消息,占一个字节
*/
private ResponseType responseType;
/**
* 数据单元长度,占一个字节
*/
private int payloadLength;
/**
* 数据单元,按照数据单元长度
*/
private String payload;
}
@Getter
public enum RequestType {
UNKNOWN((byte) 0xFF, "未知类型"),
NOTICE((byte) 0x01, "通知消息"),
;
private final byte value;
private final String description;
RequestType(byte value, String description) {
this.value = value;
this.description = description;
}
public static RequestType valueOf(byte type) {
for (RequestType t : values()) {
if (t.value == type) {
return t;
}
}
return UNKNOWN;
}
}
@Getter
@AllArgsConstructor
public enum ResponseType {
// 未知
UNKNOWN((byte) 0xFF),
// 成功
SUCCESS((byte) 0x01),
// 错误
FAILED((byte) 0x02),
;
private final byte value;
public static ResponseType valueOf(byte type) {
for (ResponseType t : values()) {
if (t.value == type) {
return t;
}
}
return UNKNOWN;
}
}
到这里协议相关部分就介绍完毕,下节将会给大家带来真正的整合示例,相信大家已经迫不及待了。