前言:
公司业务需求是相机端与java服务端通过nanomsg进行交互,而传输消息就用到了 flatbuffers/protobuf进行对消息的序列化和反序列化。
1、flatbuffers/protobuf
有人会问这两个东西到底是干什么的,你可以把json当成他两个的兄弟来理解,毕竟json还是比较大众化的。想要再网络上进行数据传输时,就需要进行序列化,即将传输对象进行流化,之后便可对对象进行读写。
序列化就是: 对象—>buffer
反序列化是: buffer—>对象
实际开发应用就是:我们相机端使用c写的,云端是java写的,双方如何相互之间发送并接收消息。这时候flatbuffers和protobuf就出来了:他们会先给双方定义一份协议,双方拿着这份协议去对应不同的语言生成不同的代码。c通过协议发送序列化后的消息给java,java拿到消息后,再根据协议进行反序列化,ok,消息就收到了,结束。
2、java+flatbuffers
1、下载flatbuffers源码
地址:源码
然后将java下的目录文件copy到项目:eg
2、下载编译器
下载地址
俺没用过苹果,只知道windows
解压后,会有 flact.exe应用程序
3、编辑schema 文件,生成java
在编译器同级目录下建一个文本文档,将下面内容写上去
namespace com.xtuer.bean;
table Cml {
name:string;
age:int;
}
root_type Cml;
记住,要将后文件后缀改为fbs
然后cmd 到这个目录下 执行:flatc --java Cml.fbs
这时候你会发现目录下生成了一个com.flat.bean.Cml.java
copy进项目
整体结构:
4、运行demo
package com.flat.bean;
import com.google.flatbuffers.FlatBufferBuilder;
import java.nio.ByteBuffer;
import java.util.Arrays;
/**
* @author mlcheng
* @date 2022/10/21 14:53
*/
public class FlatBuffersDemo {
private static final String FILE_NAME = "E:\\谷歌下载\\test.txt";
public static void main(String[] args) throws Exception {
fasong();
jieshou();
}
public static void fasong(){
// 创建 CMl对象
FlatBufferBuilder builder = new FlatBufferBuilder();
int root = Cml.createCml(builder, builder.createString("cml"), 18);
builder.finish(root);
// 序列化 对象为字节码
byte[] buffer = builder.sizedByteArray();
System.out.println(buffer);
System.out.println(Arrays.toString(buffer));
}
public static void jieshou() throws Exception {
byte[] buf = {12, 0, 0, 0, 8, 0, 12, 0, 4, 0, 8, 0, 8, 0, 0, 0, 8, 0, 0, 0, 18, 0, 0, 0, 3, 0, 0, 0, 99, 109, 108, 0};
ByteBuffer buffer = ByteBuffer.wrap(buf);
Cml cml = Cml.getRootAsCml(buffer);
System.out.println("Name: " + cml.name() + ", Age: " + cml.age());
}
}
运行发送方法:
运行接收的方法:
接收的buf就是复制发送过来的字节,结束,打通。
3、java+protobuf
1、下载idea插件
File->settings->Plugins->GenProtobuf
2、pom.xml中引入依赖
<dependency>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java</artifactId>
<version>3.5.1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.google.protobuf/protobuf-java-util -->
<dependency>
<groupId>com.google.protobuf</groupId>
<artifactId>protobuf-java-util</artifactId>
<version>3.5.1</version>
</dependency>
<!-- https://mvnrepository.com/artifact/io.grpc/grpc-all -->
<dependency>
<groupId>io.grpc</groupId>
<artifactId>grpc-all</artifactId>
<version>1.11.0</version>
</dependency>
<build>
<extensions>
<extension>
<groupId>kr.motd.maven</groupId>
<artifactId>os-maven-plugin</artifactId>
<version>1.5.0.Final</version>
</extension>
</extensions>
<plugins>
<plugin>
<groupId>org.xolstice.maven.plugins</groupId>
<artifactId>protobuf-maven-plugin</artifactId>
<version>0.5.0</version>
<configuration>
<protocArtifact>
com.google.protobuf:protoc:3.1.0:exe:${os.detected.classifier}
</protocArtifact>
<pluginId>grpc-java</pluginId>
<pluginArtifact>
io.grpc:protoc-gen-grpc-java:1.11.0:exe:${os.detected.classifier}
</pluginArtifact>
</configuration>
<executions>
<execution>
<goals>
<goal>compile</goal>
<goal>compile-custom</goal>
</goals>
</execution>
</executions>
</plugin>
</build>
3、编写.proto文件,生成java类
新建一个proto文件夹,编写一个.proto文件
syntax = "proto3";
//生成文件所在包名
option java_package = "com.xinhuan.llpr.cloud.proto";
//生成的java文件名
option java_outer_classname = "CloudProto";
message Cloud {
string name = 1;
int32 age = 2;
}
然后右边maven打开 点击protobuf:compile
这是你就会发现生成了一个.java
把这个.java复制到项目中,但是最好不要动他的路径,我之所以在.proto命名为这个路径,是因为我项目的结构就是这样的,需要根据自己的项目来定义
复制成功后:
这就是为什么不要随便移动,包的路径最好相同,因为人家自动给我们生成的东西,最好不要改了。
4、demo
package com.xinhuan.llpr.cloud.proto;
import com.google.protobuf.InvalidProtocolBufferException;
import com.google.protobuf.util.JsonFormat;
import java.util.Arrays;
/**
* @author mlcheng
* @date 2022/10/21 15:53
*/
public class ProtobufDemo {
public static void main(String[] args) throws InvalidProtocolBufferException {
CloudProto.Cloud.Builder cloudBUilder = CloudProto.Cloud.newBuilder();
cloudBUilder.setAge(18);
cloudBUilder.setName("cml");
CloudProto.Cloud cloud = cloudBUilder.build();
System.out.println("对象:\n"+cloud);
byte[] cloudBytes = cloud.toByteArray();
System.out.println("长度:"+cloudBytes.length);
System.out.println("序列化后:"+cloudBytes+"-------"+ Arrays.toString(cloudBytes));
CloudProto.Cloud cloud1 = CloudProto.Cloud.parseFrom(cloudBytes);
System.out.println("反序列化后:\n"+cloud1);
System.out.println( "序列化后转成json形式:"+ JsonFormat.printer().print(cloud1));
}
}
运行结果:
总结:
其实这两个demo 我们就能看出flatbuffers和protobuf的区别:
同样的name和age,可以明显看出protobuf的byte长度小,flatbuffers的长度长
即:flat的序列化后的数据比proto大,运行时的内存是proto的两倍左右,但就是因为他的内存大,所以他的反序列化速度嘎嘎快。有利有弊,我们最后用的是protobuf。