0


13. Springboot集成Protobuf

1、前言

在以往的项目中进行网络通信和数据交换的应用场景中,最经常使用的技术便是json或xml。随着JSON的灵活优势,越来越多的企业选择JSON作为数据交换的格式,目前JSON已经成为了业界的主流。JSON已经足够好用,且能满足相当大部分的场景。但是今天在介绍一个Google的力作protobuf作为数据交换格式。我们来看看。

2、Protobuf简介

Github地址:GitHub - protocolbuffers/protobuf: Protocol Buffers - Google's data interchange format

官网地址:Overview | Protocol Buffers Documentation

Protobuf(Protocol Buffers)是由 Google 开发的一种轻量级、高效的数据交换格式,它被用于结构化数据的序列化、反序列化和传输。相比于 XML 和 JSON 等文本格式,Protobuf 具有更小的数据体积、更快的解析速度和更强的可扩展性。同时他是一种语言无关、平台无关、可扩展的序列化格式。它使开发人员能够在文件中定义结构化数据.proto,然后使用该文件生成可以从不同数据流写入和读取数据的源代码。

2.1、核心思想

Protobuf 核心思想是使用协议来定义数据的结构和编码方式。协议是一个文本文件,其中定义了消息的结构。消息由字段组成,每个字段都有一个名称、类型和可选的默认值。然后使用Protobuf提供的解码器生成对应代码,用于序列化和反序列化数据,由于Protobuf是基于二进制编码,因此可以跨语言使用。

Protobuf 支持以下数据类型:

  • 基本类型:例如 int32、string、bool 等
  • 复合类型:例如 message、enum 等

2.2、Protobuf是如何工作的?

Protobuf 使用二进制数据格式,与基于文本的格式相比,它更紧凑且读写速度更快。它还提供了接口定义语言(IDL),可以轻松定义要序列化的数据的结构。

Protobuf 文件使用文件扩展名保存.proto。该.proto文件以 Protobuf 的 IDL 格式编写,包含有关数据结构的所有信息。数据被建模为“消息”,即名称/值对组。以下是文件中简单 Protobuf 消息的示例.proto:

// 指定 Protobuf 版本为版本 3(最新版本)
syntax = "proto3";

// 指定protobuf包名,防止类名重复
package com.shamee.protobuf;

// 生成的文件存放在哪个包下
option java_package = "com.shamee.protos";

// 生成的类名,如果没有指定,会根据文件名自动转驼峰来命名
option java_outer_classname = "PersonProtos";

// 定义了一个Person类
message Person {
    // 后面的值(=1  =2)作为序列化后的二进制编码中的字段的唯一标签
    // 因此 1-15比 16 会少一个字节,所以尽量使用 1-15 来指定常用字段。
    int32 id = 1;
    string name = 2;
    string email = 3;
    string address = 4;
}

示例中,客户消息包含四个字段:id、name、email和address。每个字段都有其类型指示,以及指示其是否为required、optional或 的标签repeated。

该.proto文件可以使用 Protoc(即 Protobuf 编译器)编译成多种编程语言。该编译器以开发人员指定的编程语言生成源代码。该源代码包括用于写入、读取和操作.proto文件中定义的消息类型的类和方法。

当有数据要存储或传输时,可以创建生成的类的实例并用您的数据填充它们。然后将这些实例序列化为二进制格式。读取数据时,二进制格式将反序列化回从.proto文件生成的类的实例。这使您可以轻松访问结构化数据。

Protobuf 生成的二进制数据格式是平台无关的,可用于在不同系统、应用程序或服务之间交换数据,即使它们是用不同的编程语言实现或在不同的平台上运行的。

2.3、如何使用 Protoc 生成代码?

上面定义好的.proto,可以使用Protobbuf编译器(Protoc)将文件编译成不同语言。

下载编译器:Release Protocol Buffers v25.3 · protocolbuffers/protobuf · GitHub

编译命令如下面的代码将.proto文件编译成 JavaScript:

protoc --js_out=import_style=commonjs,binary: .customers.proto

编译成java语言:

protoc --java_out=./my_dist  .customers.proto

3、Springboot集成

上面介绍了protobuf的基本内容,以及简单的语法编写和编译。接下来我们来使用他,并集成到我们的springboot中。

3.1、引入依赖

<dependency>
    <groupId>com.google.protobuf</groupId>
    <artifactId>protobuf-java</artifactId>
    <version>3.6.1</version>
</dependency>
<!-- 同时添加maven插件,用于编译protobuf生成java文件 -->
<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>
                <!--默认值,proto源文件路径-->
                <protoSourceRoot>${project.basedir}/src/main/resources/proto</protoSourceRoot>
                <pluginId>grpc-java</pluginId>
                <!--是否清空上面配置目录outputDirectory-->
                <clearOutputDirectory>false</clearOutputDirectory>
            </configuration>
            <executions>
                <execution>
                    <goals>
                        <goal>compile</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>

        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-jar-plugin</artifactId>
            <version>3.3.0</version>
            <configuration>
                <excludes>
                    <exclude>**/*.proto</exclude>
                </excludes>
            </configuration>
        </plugin>
    </plugins>
</build>

3.2、定义Proto文件

定义两个proto文件,一个用于接收接口请求数据Person.proto,一个用于响应Response.proto。

Person.proto:

syntax = "proto3";
package proto.shamee;

// 生成的文件存放在哪个包下
option java_package = "proto.shamee";

// 生成的类名,如果没有指定,会根据文件名自动转驼峰来命名
option java_outer_classname = "PersonProto";

// 定义了一个Person类
message Person {
    // 后面的值(=1  =2)作为序列化后的二进制编码中的字段的唯一标签
    // 因此 1-15比 16 会少一个字节,所以尽量使用 1-15 来指定常用字段。
    int32 id = 1;
    string name = 2;
    string email = 3;
    string address = 4;
}

Response.proto:

syntax = "proto3";
package proto.shamee;
option java_package = "proto.shamee";
option java_outer_classname = "ResponseProto";

message Response {
    int32 code = 1;
    string message = 2;
    string data = 3;
}

3.3、Protobuf生成Java代码

定义完后,可以直接mvn install,可以生成响应的proto的java代码。

3.4、配置Protobuf的序列化和反序列化

@Configuration
public class ProtobufConfig {

    /**
     * protobuf 序列化
     */
    @Bean
    ProtobufHttpMessageConverter protobufHttpMessageConverter() {
        return new ProtobufHttpMessageConverter();
    }

    /**
     * protobuf 反序列化
     */
    @Bean
    RestTemplate restTemplate(ProtobufHttpMessageConverter protobufHttpMessageConverter) {
        return new RestTemplate(Collections.singletonList(protobufHttpMessageConverter));
    }

}

3.5、定义controller接口

由于protobuf是基于二进制流传输数据,因此这里需要指定一下x-protobuf协议。

@RestController
@RequestMapping("/test")
public class TestController {

    @PostMapping(value = "/index", produces = "application/x-protobuf")
    public ResponseProto.Response index(@RequestBody PersonProto.Person person){

        return ResponseProto.Response.newBuilder()
                .setCode(200)
                .setMessage("OK")
                .setData("hello:" + person.getName()).build();
    }

}

接着就可以启动springboot项目啦。

3.6、访问

这里访问的时候,需要定义header的content-type,同时参数以二进制数据进行传输访问。我们的请求数据:

访问头配置:

返回:

到此我们就简单的学会了protobuf了,又可以安心的去吃夜宵了。

4、小结

protobuf在整个集成中还是有一些问题,如ptotoc的版本号如果相差太多就会编译不通过。当然protobuf也存在一些不足之处:

  • 功能简单:Protobuf 功能简单,无法用来表示复杂的概念。例如,它无法表示 XML 中的DTD 或 XSD 等复杂结构。
  • 通用性较差:Protobuf 是 Google 内部使用的工具,通用性较差。XML 和 JSON 已成为多种行业标准的编写工具,而 Protobuf 在通用性上还差很多。
  • 自解释性差:Protobuf 以二进制形式存储数据,不便于阅读和编辑。XML 具有自解释性,可以直接用文本编辑器打开和编辑。

Protobuf 是一种优秀的序列化格式,但并非完美无缺。在选择序列化格式时,需要根据实际需求进行综合考虑。如果需要一种高效、紧凑、可扩展的序列化格式,Protobuf 是一个不错的选择。但如果需要表示复杂的概念、通用性或自解释性,则需要考虑其他序列化格式。


本文转载自: https://blog.csdn.net/p793049488/article/details/136422213
版权归原作者 有一只柴犬 所有, 如有侵权,请联系我们删除。

“13. Springboot集成Protobuf”的评论:

还没有评论