• Spring Boot集成protobuf快速入门Demo


    1.什么是protobuf

    ProtobufProtocol Buffers)是由 Google 开发的一种轻量级、高效的数据交换格式,它被用于结构化数据的序列化、反序列化和传输。相比于 XML 和 JSON 等文本格式,Protobuf 具有更小的数据体积、更快的解析速度和更强的可扩展性。 Protobuf 的核心思想是使用协议(Protocol)来定义数据的结构和编码方式。使用 Protobuf,可以先定义数据的结构和各字段的类型、字段等信息,然后使用 Protobuf 提供的编译器生成对应的代码用于序列化和反序列化数据。由于 Protobuf 是基于二进制编码的,因此可以在数据传输和存储中实现更高效的数据交换,同时也可以跨语言使用。

    google-protocol-buffers-xenonstack

    Protobuf 有以下几个优势

    • 更小的数据量:Protobuf 的二进制编码通常只有 XML 和 JSON 的 1/3 到 1/10 左右,因此在网络传输和存储数据时可以节省带宽和存储空间。
    • 更快的序列化和反序列化速度:由于 Protobuf 使用二进制格式,所以序列化和反序列化速度比 XML 和 JSON 快得多。
    • 跨语言:Protobuf 支持多种编程语言,可以使用不同的编程语言来编写客户端和服务端。这种跨语言的特性使得 Protobuf 受到很多开发者的欢迎(JSON 也是如此)。
    • 易于维护可扩展:Protobuf 使用 .proto 文件定义数据模型和数据格式,这种文件比 XML 和 JSON 更容易阅读和维护,且可以在不破坏原有协议的基础上,轻松添加或删除字段,实现版本升级和兼容性。

    2.代码工程

     实验目标

    rest api实现基于Protobuf 协议通信

    pom.xml

    1. <?xml version="1.0" encoding="UTF-8"?>
    2. <project xmlns="http://maven.apache.org/POM/4.0.0"
    3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    4. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    5. <parent>
    6. <groupId>org.springframework.boot</groupId>
    7. <artifactId>spring-boot-starter-parent</artifactId>
    8. <version>3.2.1</version>
    9. </parent>
    10. <modelVersion>4.0.0</modelVersion>
    11. <artifactId>protobuf</artifactId>
    12. <properties>
    13. <maven.compiler.source>17</maven.compiler.source>
    14. <maven.compiler.target>17</maven.compiler.target>
    15. </properties>
    16. <dependencies>
    17. <dependency>
    18. <groupId>org.springframework.boot</groupId>
    19. <artifactId>spring-boot-starter-web</artifactId>
    20. </dependency>
    21. <dependency>
    22. <groupId>org.springframework.boot</groupId>
    23. <artifactId>spring-boot-starter-test</artifactId>
    24. <scope>test</scope>
    25. </dependency>
    26. <dependency>
    27. <groupId>org.springframework.boot</groupId>
    28. <artifactId>spring-boot-autoconfigure</artifactId>
    29. </dependency>
    30. <dependency>
    31. <groupId>com.google.protobuf</groupId>
    32. <artifactId>protobuf-java</artifactId>
    33. <version>3.19.2</version>
    34. </dependency>
    35. <dependency>
    36. <groupId>org.springframework.boot</groupId>
    37. <artifactId>spring-boot-test</artifactId>
    38. <scope>test</scope>
    39. </dependency>
    40. </dependencies>
    41. <build>
    42. <extensions>
    43. <extension>
    44. <groupId>kr.motd.maven</groupId>
    45. <artifactId>os-maven-plugin</artifactId>
    46. <version>1.6.1</version>
    47. </extension>
    48. </extensions>
    49. <plugins>
    50. <plugin>
    51. <groupId>org.xolstice.maven.plugins</groupId>
    52. <artifactId>protobuf-maven-plugin</artifactId>
    53. <version>0.6.1</version>
    54. <configuration>
    55. <protoSourceRoot>${basedir}/src/main/resources</protoSourceRoot>
    56. <protocArtifact>com.google.protobuf:protoc:3.19.1:exe:${os.detected.classifier}</protocArtifact>
    57. <pluginArtifact>io.grpc:protoc-gen-grpc-java:1.43.1:exe:${os.detected.classifier}</pluginArtifact>
    58. <outputDirectory>src/main/java</outputDirectory>
    59. <clearOutputDirectory>false</clearOutputDirectory>
    60. <pluginId>grpc-java</pluginId>
    61. </configuration>
    62. <executions>
    63. <execution>
    64. <goals>
    65. <goal>compile</goal>
    66. <goal>compile-custom</goal>
    67. </goals>
    68. </execution>
    69. </executions>
    70. </plugin>
    71. <plugin>
    72. <groupId>org.springframework.boot</groupId>
    73. <artifactId>spring-boot-maven-plugin</artifactId>
    74. </plugin>
    75. </plugins>
    76. </build>
    77. </project>

    controller

    1. package com.et.protobuf.controller;
    2. import com.et.protobuf.PhoneNumJson;
    3. import com.et.protobuf.ProtobufMessage;
    4. import com.et.protobuf.StudentJson;
    5. import org.springframework.web.bind.annotation.PathVariable;
    6. import org.springframework.web.bind.annotation.RequestMapping;
    7. import org.springframework.web.bind.annotation.RestController;
    8. import java.util.ArrayList;
    9. import java.util.HashMap;
    10. import java.util.List;
    11. import java.util.Map;
    12. @RestController
    13. public class HelloWorldController {
    14. @RequestMapping("/json/{id}")
    15. public StudentJson showHelloWorld(@PathVariable Integer id){
    16. StudentJson studentJson = new StudentJson();
    17. studentJson.setId(id);
    18. studentJson.setFirstName("maxsm");
    19. studentJson.setLastName("sdfsdfsdfsdfsdfsdsdfsdfsdfsdfsdfsdfsdfsdf");
    20. studentJson.setEmail("1224sdfsfsdf344552@163.com");
    21. PhoneNumJson phoneNumJson = new PhoneNumJson();
    22. phoneNumJson.setNumber("12345sdfsdfsd6566666");
    23. phoneNumJson.setType(1);
    24. List list = new ArrayList<>();
    25. list.add(phoneNumJson);
    26. studentJson.setPhoneNumList(list);
    27. return studentJson;
    28. }
    29. @RequestMapping("/protobuf/{id}")
    30. ProtobufMessage.Student protobuf(@PathVariable Integer id) {
    31. return ProtobufMessage.Student.newBuilder().setId(id).setFirstName("maxsm")
    32. .setLastName("sdfsdfsdfsdfsdfsdsdfsdfsdfsdfsdfsdfsdfsdf")
    33. .setEmail("1224sdfsfsdf344552@163.com")
    34. .addPhone(ProtobufMessage.Student.PhoneNumber.newBuilder().setNumber("12345sdfsdfsd6566666").setType(
    35. ProtobufMessage.Student.PhoneType.MOBILE).build()).build();
    36. }
    37. }

    entity

    1. package com.et.protobuf;
    2. import java.util.List;
    3. /**
    4. * @author liuhaihua
    5. * @version 1.0
    6. * @ClassName StudentJson
    7. * @Description todo
    8. * @date 2024/08/05/ 16:32
    9. */
    10. public class StudentJson {
    11. private int id;
    12. private String firstName;
    13. private String lastName;
    14. private String email;
    15. private List<PhoneNumJson> phoneNumList;
    16. public int getId() {
    17. return id;
    18. }
    19. public void setId(int id) {
    20. this.id = id;
    21. }
    22. public String getFirstName() {
    23. return firstName;
    24. }
    25. public void setFirstName(String firstName) {
    26. this.firstName = firstName;
    27. }
    28. public String getLastName() {
    29. return lastName;
    30. }
    31. public void setLastName(String lastName) {
    32. this.lastName = lastName;
    33. }
    34. public String getEmail() {
    35. return email;
    36. }
    37. public void setEmail(String email) {
    38. this.email = email;
    39. }
    40. public List<PhoneNumJson> getPhoneNumList() {
    41. return phoneNumList;
    42. }
    43. public void setPhoneNumList(List phoneNumList) {
    44. this.phoneNumList = phoneNumList;
    45. }
    46. }
    1. package com.et.protobuf;
    2. /**
    3. * @author liuhaihua
    4. * @version 1.0
    5. * @ClassName PhoneNum
    6. * @Description todo
    7. * @date 2024/08/05/ 16:35
    8. */
    9. public class PhoneNumJson {
    10. private int type;
    11. private String number;
    12. public int getType() {
    13. return type;
    14. }
    15. public void setType(int type) {
    16. this.type = type;
    17. }
    18. public String getNumber() {
    19. return number;
    20. }
    21. public void setNumber(String number) {
    22. this.number = number;
    23. }
    24. }

    mxsm.proto

    使用 Protobuf 的语言定义文件(.proto)可以定义要传输的信息的数据结构,可以包括各个字段的名称、类型等信息。同时也可以相互嵌套组合,构造出更加复杂的消息结构。

    1. syntax = "proto3";
    2. package mxsm;
    3. option java_package = "com.et.protobuf";
    4. option java_outer_classname = "ProtobufMessage";
    5. message Course {
    6. int32 id = 1;
    7. string course_name = 2;
    8. repeated Student student = 3;
    9. }
    10. message Student {
    11. int32 id = 1;
    12. string first_name = 2;
    13. string last_name = 3;
    14. string email = 4;
    15. repeated PhoneNumber phone = 5;
    16. message PhoneNumber {
    17. string number = 1;
    18. PhoneType type = 2;
    19. }
    20. enum PhoneType {
    21. MOBILE = 0;
    22. LANDLINE = 1;
    23. }
    24. }
    头部全局定义
    • syntax = "proto3"; 指定 Protobuf 版本为版本 3(最新版本)
    • package com.wdbyte.protobuf; 指定 Protobuf 包名,防止有相同类名的 message 定义,这个包名是生成的类中所用到的一些信息的前缀,并非类所在包。
    • option java_multiple_files = true; 是否生成多个文件。若 false,则只会生成一个类,其他类以内部类形式提供。
    • option java_package = 生成的类所在包。
    • option java_outer_classname 生成的类名,若无,自动使用文件名进行驼峰转换来为类命名。
    消息结构具体定义

    message Person 定一个了一个 Person 类。 Person 类中的字段被 optional 修饰,被 optional 修饰说明字段可以不赋值。

    • 修饰符 optional 表示可选字段,可以不赋值。
    • 修饰符 repeated 表示数据重复多个,如数组,如 List。
    • 修饰符 required 表示必要字段,必须给值,否则会报错 RuntimeException,但是在 Protobuf 版本 3 中被移除。即使在版本 2 中也应该慎用,因为一旦定义,很难更改。
    字段类型定义

    修饰符后面紧跟的是字段类型,如 int32 、string。常用的类型如下:

    • int32、int64、uint32、uint64:整数类型,包括有符号和无符号类型。
    • float、double:浮点数类型。
    • bool:布尔类型,只有两个值,true 和 false。
    • string:字符串类型。
    • bytes:二进制数据类型。
    • enum:枚举类型,枚举值可以是整数或字符串。
    • message:消息类型,可以嵌套其他消息类型,类似于结构体。

    字段后面的 =1,=2 是作为序列化后的二进制编码中的字段的对应标签,因为 Protobuf 消息在序列化后是不包含字段信息的,只有对应的字段序号,所以节省了空间。也因此,1-15 比 16 会少一个字节,所以尽量使用 1-15 来指定常用字段。且一旦定义,不要随意更改,否则可能会对不上序列化信息

    config

    1. package com.et.protobuf.config;
    2. import org.springframework.context.annotation.Bean;
    3. import org.springframework.context.annotation.Configuration;
    4. import org.springframework.http.converter.protobuf.ProtobufHttpMessageConverter;
    5. import org.springframework.web.client.RestTemplate;
    6. import java.util.Arrays;
    7. @Configuration
    8. public class Config {
    9. @Bean
    10. RestTemplate restTemplate(ProtobufHttpMessageConverter hmc) {
    11. return new RestTemplate(Arrays.asList(hmc));
    12. }
    13. @Bean
    14. ProtobufHttpMessageConverter protobufHttpMessageConverter() {
    15. return new ProtobufHttpMessageConverter();
    16. }
    17. }

    以上只是一些关键代码,所有代码请参见下面代码仓库

    代码仓库

    3.测试

    启动Spring Boot应用

    编写测试类

    1. package com.et.protobuf;
    2. import org.junit.jupiter.api.Test;
    3. import org.springframework.beans.factory.annotation.Autowired;
    4. import org.springframework.boot.test.context.SpringBootTest;
    5. import org.springframework.http.ResponseEntity;
    6. import org.springframework.http.converter.HttpMessageConverter;
    7. import org.springframework.http.converter.protobuf.ProtobufHttpMessageConverter;
    8. import org.springframework.web.client.RestTemplate;
    9. import java.util.ArrayList;
    10. import java.util.List;
    11. @SpringBootTest(classes = DemoApplication.class)
    12. public class ApplicationTest {
    13. // Other declarations
    14. private static final String COURSE1_URL = "http://localhost:8088/protobuf/1";
    15. @Autowired
    16. private RestTemplate restTemplate ;
    17. @Test
    18. public void whenUsingRestTemplate_thenSucceed() {
    19. ResponseEntity student = restTemplate.getForEntity(COURSE1_URL, ProtobufMessage.Student.class);
    20. System.out.println(student.toString());
    21. }
    22. }

    结果如下

    1. <200 OK OK,id: 1
    2. first_name: "maxsm"
    3. last_name: "sdfsdfsdfsdfsdfsdsdfsdfsdfsdfsdfsdfsdfsdf"
    4. email: "1224sdfsfsdf344552@163.com"
    5. phone {
    6. number: "12345sdfsdfsd6566666"
    7. }
    8. ,[X-Protobuf-Schema:"mxsm.proto", X-Protobuf-Message:"mxsm.Student", Content-Type:"application/x-protobuf;charset=UTF-8", Transfer-Encoding:"chunked", Date:"Mon, 05 Aug 2024 09:10:55 GMT", Keep-Alive:"timeout=60", Connection:"keep-alive"]>

    Json和protobuf性能比较

    单个线程,循环1分钟

    json

    Protobuf

    测试结果发现json性能甚至优于protobuf,并不符合预期结果!是我哪一步整错了吗?有大神知道怎么回事吗?

    4.引用

  • 相关阅读:
    计算机毕业设计Java海康物流(源码+系统+mysql数据库+lw文档)
    密码学与网络安全:量子计算的威胁与解决方案
    扩散模型(Diffusion Model,DDPM,GLIDE,DALLE2,Stable Diffusion)
    【前端】CSS-Grid网格布局
    第一章 教育基础(07 心理学基础知识)
    【深入浅出 Yarn 架构与实现】2-1 Yarn 基础库概述
    基于Java web的校园滴滴代驾管理系统 毕业设计-附源码260839
    Python Pandas简介及基础教程+实战示例。
    SpringCloud Gateway与Zuul的取舍选择及其工作流程
    Laravel 6 - 第十七章 配置数据库
  • 原文地址:https://blog.csdn.net/dot_life/article/details/140965295