Protobuf - 基本应用程序

现在让我们使用 Google Protocol Buffer,看看它如何与简单的 Greeting 应用程序配合使用。在这个例子中,我们将创建一个简单的应用程序,它将执行以下操作 −

  • 问候写入者 −

    • 从用户那里获取问候语和用户名

    • 将上述信息存储在磁盘中的文件中

  • 问候读取者 −

    • 读取我们存储在上述文件中的相同文件

    • 将该数据转换为对象并打印数据

协议缓冲区定义文件

协议缓冲区"定义文件"包含我们要序列化的数据的模式定义。数据存储在扩展名为 ".proto" 的人类可读文件中。

让我们将以下数据存储在 "greeting.proto" 中,我们将在我们的第一个应用程序中使用它。

syntax = "proto3";
package tutorial;
option java_package = "com.tutorialspoint.greeting";

message Greet {
   string greeting = 1;
   string username = 2;
}

现在,让我们仔细看看数据,看看上面代码块中每行代码的作用。

syntax = "proto3";

这里的"syntax"代表我们正在使用的 Protobuf 版本。因此,我们使用最新版本 3,因此模式可以使用对版本 3 有效的所有语法。

package tutorial;

这里的package用于解决冲突,例如,我们有多个同名的类/成员。

option java_package = "com.tutorialspoint.greeting";

此参数特定于 Java,即,将自动生成".proto"文件中的代码的包。

message Greet

将创建/重新创建的对象的基类的名称。

string Greeting = 1;
string username = 2;

这些是 Greet 类的属性以及数据类型和标签在架构中的位置。如果要添加新标签,则其位置应为"3"。请注意,此位置整数对于确保实际数据紧凑且存在模式演变范围非常重要。

协议缓冲区代码生成

现在我们已经定义,让我们安装"proto"二进制文件,我们将使用它来自动生成上述 Greet 类的代码。二进制文件可以在 "https://github.com/protocolbuffers/protobuf/releases/"

找到。

根据操作系统选择正确的二进制文件。我们将在 Windows 上安装 proto 二进制文件,但对于 Linux,步骤没有太大不同。

安装后,请确保您能够通过命令行访问它 −

protoc --version

libprotoc 3.15.6

它确认 Protobuf 已正确安装。现在让我们转到 创建 上面描述的 Java 问候应用程序。

项目结构

以下是我们将拥有的整体项目结构 −

与各个语言相关的代码转到各自的目录。我们有一个单独的目录来存储我们的"proto"文件。

项目结构

以下是我们将拥有的 Java 项目结构 −

项目结构1

Java 中的问候应用程序

现在我们已经安装了 protoc,我们可以使用 protoc 从 proto 文件自动生成代码。不过,我们先创建一个 Java 项目。

以下是我们将用于 Java 项目的 Maven 配置。请注意,它还包含 Protobuf 所需的库。

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	
   <modelVersion>4.0.0</modelVersion>
   <groupId>com.tutorials.point</groupId>
   <artifactId>protobuf-tutorial</artifactId>
   <version>1.0</version>
   <packaging>jar</packaging>

   <properties>
      <maven.compiler.source>1.8</maven.compiler.source>
      <maven.compiler.target>1.8</maven.compiler.target>
   </properties>

   <dependencies>
      <!-- https://mvnrepository.com/artifact/com.google.protobuf/protobuf-java -->
      <dependency>
         <groupId>com.google.protobuf</groupId>
         <artifactId>protobuf-java</artifactId>
         <version>3.15.8</version>
      </dependency>
   </dependencies>

   <build>
      <plugins>
         <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-shade-plugin</artifactId>
            <version>3.2.4</version>
            <configuration>
               <!--Put your configurations here-->
            </configuration>
            <executions>
               <execution>
                  <phase>package</phase>
                     <goals>
                     <goal>shade</goal>
                  </goals>
               </execution>
            </executions>
         </plugin>
      </plugins>
   </build>
</project>

我们所有的代码都位于 src/main/java 下。

项目结构搞定后,让我们为 Greet 类生成代码 −

protoc --java_out=java/src/main/java proto_files/greeting.proto

执行命令后,您会注意到两个自动生成的类。

  • Greeting.java

  • GreetOrBuilder.java

这两个类将帮助我们对 Greet 对象进行序列化和反序列化。

现在,让我们 编写 数据的编写器,它将用户名问候语作为其输入 −

package com.tutorialspoint.greeting;

import java.io.FileOutputStream;
import java.io.IOException;
import com.tutorialspoint.greeting.Greeting.Greet;

public class GreetWriter{
   public static void main(String[] args) throws IOException {
      Greet greeting = Greet.newBuilder()
         .setGreeting(args[0])
         .setUsername(args[1])
         .build();
		
      String filename = "greeting_protobuf_output";
      System.out.println("Saving greeting to file: " + filename);
		
      try(FileOutputStream output = new FileOutputStream(filename)){
         greeting.writeTo(output);
      }
      System.out.println("Saved greeting with following data to disk: 
" + greeting);
   }
}

writer 仅接受 CLI 参数,创建 Greet 对象,对其进行序列化,然后将其转储到文件中。

现在让我们编写一个 reader 来读取文件 −

package com.tutorialspoint.greeting;

import java.io.FileInputStream;
import java.io.IOException;
import com.tutorialspoint.greeting.Greeting.Greet;

public class GreetReader{
   public static void main(String[] args) throws IOException {
      Greet.Builder greetBuilder = Greet.newBuilder();

      String filename = "greeting_protobuf_output";
      System.out.println("Reading from file " + filename);
	    
      try(FileInputStream input = new FileInputStream(filename)) {
         Greet greet = greetBuilder.mergeFrom(input).build();
         System.out.println("Greeting: " + greet.getGreeting() + "
" + "Username: " + greet.getUsername());
      }
   }
}

读取器只是从同一个文件中读取,对其进行反序列化,然后打印有关问候语的数据。

现在我们已经设置了读取器写入器,让我们编译该项目。

mvn clean install

现在,让我们首先执行写入器。

java -cp .	arget\protobuf-tutorial-1.0.jar com.tutorialspoint.greeting.GreetWriter Hello John

Saving greeting to file: 
greeting_protobuf_output

Saved greeting with following data to disk:
greeting: Hello
username: John

然后,让我们执行读取器。

java -cp .	arget\protobuf-tutorial-1.0.jar com.tutorialspoint.greeting.GreetReader

Reading from file greeting_protobuf_output
Greeting: Hello
Username: John

因此,当我们看到由 writer 序列化并保存到文件的数据时,该确切数据已由 reader 正确反序列化并相应地打印。

Python 中的问候应用程序

现在让我们将相同的示例编写为 Python 项目 −

在继续之前,我们需要安装 protobuf pip 包

pip install protobuf

我们所有的代码都将出现在 "google-protobuf/python" 下。

在项目结构完成后,让我们为 Greet 类生成代码 −

protoc --python_out=python proto_files/greeting.proto

执行此操作后命令,您会注意到 Python 目录下有一个自动生成的类 "*proto_files/greeting_pb2.py"。此类将帮助我们对 Greet 对象进行序列化和反序列化。

现在,让我们编写数据的编写器,它将以 用户名问候语 作为其输入 −

from .proto_files import greeting_pb2

import sys

greet = greeting_pb2.Greet()
greet.username = sys.argv[1]
greet.greeting = sys.argv[2]

filename = "greeting_protobuf_output";
print("Saving to file: " + filename)

f = open(filename, "wb")
f.write(greet.SerializeToString())
f.close()
print("Saved following greeting to disk: 
" + str(greet))

writer 仅接受 CLI 参数,创建 Greet 对象,对其进行序列化,然后将其转储到文件中。

现在让我们创建一个 reader 来读取文件 −

from proto_files import greeting_pb2

greet = greeting_pb2.Greet()

filename = "greeting_protobuf_output";
print("Reading from file: " + filename)

f = open(filename, "rb")
greet.ParseFromString(f.read())
f.close()

print("Read greeting from disk: 
" + str(greet))

读取器只是从同一个文件中读取,对其进行反序列化,然后打印有关问候的数据。

现在,让我们首先执行写入器。

python greetWriter.py Hola Jane

Saving to file: greeting_protobuf_output
Saved following greeting to disk:
greeting: "Hola"
username: "Jane"

And then, let us execute the reader.

python greetReader.py

Reading from file: greeting_protobuf_output
Read greeting from disk:
greeting: "Hola"
username: "Jane"

因此,如我们所见,数据由 writer 序列化并保存到文件中。接下来,相同的数据由 reader 正确反序列化并相应地打印。