内容摘抄自书籍《Netty redis zookeeper高并发实战》

Protobuf使用

proto文件来预先定义的消息格式。数据包是按照proto文件所定义的消息格式完成二进制码流的编码和解码。proto文件,简单地说,就是一个消息的协议文件,这个协议文件的后缀文件名为“.proto”。 作为演示,下面介绍一个非常简单的proto文件:仅仅定义一个消息结构体,并且该消息结构体也非常简单,仅包含两个字段。实例如下:

 // [开始头部声明]
syntax = "proto3";
packagecom.crazymakercircle.netty.protocol;
// [结束头部声明]
// [开始 java选项配置]
option java_package = "com.crazymakercircle.netty.protocol";
option java_outer_classname = "MsgProtos";
// [结束 java选项配置]
// [开始消息定义]
message Msg {uint32 id = 1;  //消息IDstring content = 2;//消息内容
}
// [结束消息定义]

例子:

syntax = "proto3";
message Model {int64 id = 1;string action = 2;string content = 3;string sender = 4;string receiver = 5;string extra = 6;string title = 7;string format = 8;int64 timestamp = 9;
}

注:在idea中下载protobuf插件即可以高亮proto文件

在“.proto”文件的头部声明中,需要声明“.proto”所使用的Protobuf协议版本,这里使用的是"proto3"。也可以使用旧一点的版本"proto2",两个版本的消息格式有一些细微的不同。
默认的协议版本为"proto2"。

Protobuf支持很多语言,所以它为不同的语言提供了一些可选的声明选项,选项的前面有option关键字。

“java_package”选项的作用为:在生成“proto”文件中消息的POJO类和Builder(构造者)的Java代码时,将Java代码放入指定的package中。

“java_outer_classname”选项的作用为:在生成“proto”文件所对应Java代码时,所生产的Java外部类的名称。 在“proto”文件中,使用message这个关键字来定义消息的结构体。在生成“proto”对应的Java代码时,
每个具体的消息结构体都对应于一个最终的Java POJO类。消息结构体的字段对应到POJO类的属性。也就是说,每定义一个“message”结构体相当于声明一个Java中的类。并且message中可以内嵌message,就像java的内部类一样。 每一个消息结构体可以有多个字段。定义一个字段的格式,简单来说就是“类型名称=编号”。
例如“string content=2;”,表示该字段是string类型,名为content,序号为2。字段序号表示为:在Protobuf数据包的序列化、反序列化时,该字段的具体排序。 在每一个“.proto”文件中,可以声明多个“message”。大部分情况下,会把有依赖关系或者包含关系的message消息结构体写入一个.proto文件。将那些没有关联关系的message消息结构体,分别写入不同的文件,这样便于管理。

Maven插件生成POJO和Builder

使用命令行生成Java类的操作比较烦琐。另一种更加方便的方式是:使用protobuf-maven-plugin插件,它可非常方便地生成消息的POJO类和Builder(构造者)类的Java代码。在Maven的pom文件中增加此plugin插件的配置项,具体如下:

<plugin><groupId>org.xolstice.maven.plugins</groupId><artifactId>protobuf-maven-plugin</artifactId><version>0.5.0</version><extensions>true</extensions><configuration><!--proto文件路径--><protoSourceRoot>${project.basedir}/protobuf</protoSourceRoot><!--目标路径--><outputDirectory>${project.build.sourceDirectory}</outputDirectory><!--设置是否在生成Java文件之前清空outputDirectory的文件--><clearOutputDirectory>false</clearOutputDirectory><!--临时目录--><temporaryProtoFileDirectory>${project.build.directory}/protoc-temp</temporaryProtoFileDirectory><!--protoc可执行文件路径--><protocExecutable>${project.basedir}/protobuf/protoc3.6.1.exe</protocExecutable></configuration><executions><execution><goals><goal>compile</goal><goal>test-compile</goal></goals></execution></executions>
</plugin>

protobuf-maven-plugin插件的配置项,具体介绍如下:
·protoSourceRoot:“proto”消息结构体文件的路径。
·outputDirectory:生成的POJO类和Builder类的目标路径。
·protocExecutable:Java代码生成工具的protoc3.6.1.exe可执行文件的路径。
配置好之后,执行插件的compile命令,Java代码就利索生成了。或者在Maven的项目编译时,POJO类和Builder类也会自动生成。

1.使用Builder构造者,构造POJO消息对象

package com.crazymakercircle.netty.protocol;
//...
public class ProtobufDemo {public static MsgProtos.MsgbuildMsg() {MsgProtos.Msg.BuilderpersonBuilder = MsgProtos.Msg.newBuilder();personBuilder.setId(1000);personBuilder.setContent("疯狂创客圈:高性能学习社群");MsgProtos.Msg message = personBuilder.build();
return message;}//…..
}

Protobuf为每个message消息结构体生成的Java类中,包含了一个POJO类、一个Builder类。
构造POJO消息,首先需要使用POJO类的newBuilder静态方法获得一个Builder构造者。每一个POJO字段的值,需要通过Builder构造者的setter方法去设置。注意,消息POJO对象并没有setter方法。字段值设置完成之后,使用构造者的build()方法构造出POJO消息对象。

2.序列化serialization & 反序列化Deserialization的方式

一 获得消息POJO的实例之后,可以通过多种方法将POJO对象序列化成二进制字节,或者反序列化。下面是方式一:

package com.crazymakercircle.netty.protocol;
//...
public class ProtobufDemo {//第1种方式:序列化 serialization &反序列化 Deserialization@Testpublic void serAndDesr1() throws IOException {MsgProtos.Msg message = buildMsg();//将Protobuf对象序列化成二进制字节数组byte[] data = message.toByteArray();//可以用于网络传输,保存到内存或外存ByteArrayOutputStreamoutputStream = new ByteArrayOutputStream();outputStream.write(data);data = outputStream.toByteArray();//二进制字节数组反序列化成Protobuf对象MsgProtos.MsginMsg = MsgProtos.Msg.parseFrom(data);Logger.info("id:=" + inMsg.getId());Logger.info("content:=" + inMsg.getContent());}
//….
}

这种方式通过调用POJO对象的toByteArray()方法将POJO对象序列化成字节数组。通过调用parseFrom(byte[] data)方法,Protobuf也可以从字节数组中重新反序列化得到POJO新的实例。

3.序列化serialization & 反序列化Deserialization的方式二

package com.crazymakercircle.netty.protocol;
//...
public class ProtobufDemo {//…//第2种方式:序列化 serialization &反序列化 Deserialization@Testpublic void serAndDesr2() throws IOException {MsgProtos.Msg message = buildMsg();//序列化到二进制码流ByteArrayOutputStreamoutputStream = new ByteArrayOutputStream();message.writeTo(outputStream);ByteArrayInputStreaminputStream =new ByteArrayInputStream(outputStream.toByteArray());//从二进码流反序列化成Protobuf对象MsgProtos.MsginMsg = MsgProtos.Msg.parseFrom(inputStream);Logger.info("id:=" + inMsg.getId());Logger.info("content:=" + inMsg.getContent());}
//….
}

这种方式通过调用POJO对象的writeTo(OutputStream)方法将POJO对象的二进制字节写出到输出流。通过调用parseFrom(InputStream)方法,Protobuf从输入流中读取二进制码流重新反序列化,得到POJO新的实例。 在阻塞式的二进制码流传输应用场景中,这种序列化和反序列化的方式是没有问题的。例如,可以将二进制码流写入阻塞式的Java OIO套接字或者输出到文件。但是,这种方式在异步操作的NIO应用场景中,存在着粘包/半包的问题。

4.序列化serialization &反序列化Deserialization的方式三

package com.crazymakercircle.netty.protocol;
//...
public class ProtobufDemo {//…
//第3种方式:序列化 serialization &反序列化 Deserialization//带字节长度:[字节长度][字节数据],解决粘包/半包问题@Testpublic void serAndDesr3() throws IOException {MsgProtos.Msg message = buildMsg();//序列化到二进制码流ByteArrayOutputStreamoutputStream = new ByteArrayOutputStream();message.writeDelimitedTo(outputStream);ByteArrayInputStreaminputStream = new ByteArrayInputStream(outputStream.toByteArray());//从二进码流反序列化成Protobuf对象MsgProtos.MsginMsg = MsgProtos.Msg.parseDelimitedFrom(inputStream);Logger.info("id:=" + inMsg.getId());Logger.info("content:=" + inMsg.getContent());}
}

这种方式通过调用POJO对象的writeDelimitedTo(OutputStream)方法在序列化的字节码之前添加了字节数组的长度。这一点类似于前面介绍的Head-Content协议,只不过Protobuf做了优化,长度的类型不是固定长度的int类型,而是可变长度varint32类型。 反序列化时,调用parseDelimitedFrom(InputStream)方法。Protobuf从输入流中先读取varint32类型的长度值,然后根据长度值读取此消息的二进制字节,再反序列化得到POJO新的实例。 这种方式可以用于异步操作的NIO应用场景中,解决了粘包/半包的问题。

proto的介绍和基础使用相关推荐

  1. 理正地基基础计算机辅助设计的英文缩写,理正基础CAD软件介绍理正基础CAD软件介绍.pdf...

    理正基础CAD软件介绍理正基础CAD软件介绍 理正基础CAD 软件介绍 北京理正软件设计研究院 华中.华北地区:万涛 联 系 方 式:010 手 机 传 真:010 电 子 信 箱:lizhengwa ...

  2. 使用Julia进行图像处理--JuliaImages介绍与基础使用

    使用Julia进行图像处理--JuliaImages介绍与基础使用 安装包 读取图像 从磁盘读取单个图像 从URL读取单个图像 批量读取文件夹中的图像 保存图像 使用TestImages 预览图像 裁 ...

  3. HTML基础介绍和基础骨架

    HTML基础介绍和基础骨架 HTML的概念 HTML 全称为 HyperText Markup Language,译为超文本标记语言. HTML 不是一种编程语言,是一种描述性的标记语言. 作用:HT ...

  4. html文档主体的根标签,HTML详细介绍(基础标签篇)

    今天下午阳光明媚,北京少有的好天气.正好有机会总结HTML标签,下面就详细介绍一下基础的标签的用法及相关的属性.关于基础篇的总结部分点击这里. 基础标签的内容 :定义,声明文档类型 :html文档的根 ...

  5. 冲击红队第一天 - Web安全介绍与基础入门

    大家好! 我是小黄,很高兴又跟大家见面啦 ! 拒绝水文,从我做起 !!!! 未经允许,禁止转载 ,违者必究!!!! 本实验仅适用于学习和测试 ,严禁违法操作 ! ! ! 今天更新的是: 冲击红队第一天 ...

  6. Qt+ECharts开发笔记(五):ECharts的动态排序柱状图介绍、基础使用和Qt封装Demo

    若该文为原创文章,转载请注明原文出处 本文章博客地址:https://hpzwl.blog.csdn.net/article/details/127171413 红胖子(红模仿)的博文大全:开发技术集 ...

  7. UE4 Material 101学习笔记——01-07 介绍/PBR基础/UV扭曲/数据类型/翻页动画/材质混合/性能优化

    UE4 Material 101学习笔记--01-07 介绍/PBR基础/UV扭曲/数据类型/翻页动画/材质混合/性能优化 Lec 01 什么是着色器 What Is A Shader? 1.1 介绍 ...

  8. SQL(一)- 数据库介绍与基础操作

    数据库介绍 一.常用的数据库分为两大类: 关系型数据库 非关系型数据库(NoSql) 关系型数据库 概念:是建立在关系模型基础上的数据库,借助于集合代数等数学概念和方法来处理数据库中的数据. 关系型数 ...

  9. VRP平台总体介绍及基础配置

    前言 1.VRP软件系统基础 VRP系统在启动时需要加载"系统软件"和"配置文件"两部分,这与其它品牌网络交换机的操作系统是一样的.如果指定了下次启动的补丁文件 ...

最新文章

  1. 启动pip时,< Fatal error in launcher: Unable to create process using ‘“‘ >问题的原因及解决方法
  2. 数据结构 非线性结构 树 介绍及存储方法
  3. Redhat 停止sendmail的方法
  4. 想和你一起为 Visual Studio 庆祝20岁生日
  5. 怎么在mysql中打开表存信息,我应该如何存储用户的“收藏夹”在mySQL表?
  6. 联想g470笔记本开vt_最“发泄”评测!飞刀铁拳暴力输出,联想本结局竟然大亮?(附视频)...
  7. 【leetcode】726. Number of Atoms
  8. python如何连续查找字符串_在另一个字符串Python中多次查找字符串
  9. 图片流_干货来袭!web前端开发工程师必看之如何使用CSS3实现瀑布流效果?
  10. 计算机硬件设施落后,论中等职业学校计算机教学现状及解决方案
  11. MRP游戏软件常见问题解答以及破解方法!(新手必看)
  12. 转动惯量与惯性矩(截面)比较
  13. (转载)基于STM32的多节锂电池管理系统的设计
  14. 使用鼠标右击Gite Bash Here 将本地文件上传到 GitHub
  15. 安装计算机一级出现appcrash,问题事件名称: APPCRASH 终于解决了。
  16. 汽车之家和易车该如何走下去?
  17. 为什么要创业,有人盆满钵满、有人别无选择,区别在创业思维
  18. 量化金融-分类数据的检验
  19. win7、win10关闭驱动签名,进入驱动测试模式,以及常见初级问题的解决
  20. 一、C++面向对象高级编程(上) (侯捷)

热门文章

  1. 软件工程与项目管理的关系_PM:软件项目管理概要
  2. arcgis导入excel数据_导入Excel数据到ArcGIS属性表的两种实用方法
  3. aes key长度_Go 语言 map 解析之 key 的定位核心流程
  4. 学计算机对显卡要求大吗,【5人回答】学AE的电脑配置要求高吗?需要什么样的电脑配置-3D溜溜网...
  5. linux获取文件引用计数,linux-2.6内核模块引用计数的实现
  6. java批量执行查询sql语句_详解MyBatis直接执行SQL查询及数据批量插入
  7. 教你使用TensorFlow2对阿拉伯语手写字符数据集进行识别
  8. 四十五、Redis数据库学习
  9. 深圳内推 | ​IDEA数字经济研究院招聘NLP算法工程师/算法实习生
  10. PW Live 直播 | 清华大学博士生岂凡超:义原知识库的应用和扩充