高效编解码协议之protobuf协议详解

Protocol Buffers(简称 protobuf)是 Google 开发的 高效二进制序列化工具,用于结构化数据的存储和传输。

核心特性

特性 说明
跨语言支持 支持 Java、C++、Python、Go 等主流语言。
高效编解码 二进制格式,比 JSON/XML 更小、更快。
强类型约束 通过 .proto 文件定义数据结构,避免运行时错误。
向后兼容 支持字段扩展(新增字段不影响旧代码)。

使用指南

开发环境

  • windows
  • jetbrains idea
  • maven_3.9.7
  • jdk_8
  • protobuf_31.1

安装编译器(protoc)

配置

  • 将下载的压缩包解压出来,新增环境变量protobuf=文件夹路径
  • 编辑环境变量Path,新增%protobuf%\bin

验证

shell 复制代码
# 打开cmd命令行,执行以下命令
protoc --version

# 输出结果如下,表示安装成功
libprotoc 31.1

定义数据结构

  • 新建person.proto文件
protobuf 复制代码
// person.proto
syntax = "proto3";

message Person {
  string name = 1;
  int32 id = 2;
  repeated string emails = 3;
}

编译

手动执行命令

shell 复制代码
# 打开cmd命令行,切换到person.proto文件目录下,执行以下命令
protoc --java_out=./ ./person.proto

# 当前目录下会生成PersonOuterClass.java文件
# 将PersonOuterClass.java复制到对应项目目录就可以使用了
# PersonOuterClass.java默认没有package路径,需要手动加一下

maven插件编译

  • 在pom.xml中加入以下代码,执行maven的clean compile
xml 复制代码
<build>
  <plugins>
    <plugin>
      <groupId>org.xolstice.maven.plugins</groupId>
      <artifactId>protobuf-maven-plugin</artifactId>
      <version>0.6.1</version>
      <executions>
        <execution>
          <goals>
            <goal>compile</goal>
          </goals>
        </execution>
      </executions>
      <configuration>
        <!-- 从 Path 中查找 -->
        <protocExecutable>protoc</protocExecutable>
        <!-- 或直接指定路径 -->
        <!--<protocExecutable>xxx\bin\protoc.exe</protocExecutable>-->
      </configuration>
    </plugin>
  </plugins>
</build>
  • 插件默认读取文件夹/src/main/proto下的proto文件去编译
  • 在文件夹target/generated-sources/protobuf/java下会生成对应pojo的protobuf操作类

注意:

  1. 若执行protoc命令之后idea报如下错误:Module 'my-test' production: java.lang.ClassCastException: class org.jetbrains.jps.builders.java.dependencyView.TypeRepr$PrimitiveType cannot be cast to class org.jetbrains.jps.builders.java.dependencyView.TypeRepr$ClassType (org.jetbrains.jps.builders.java.dependencyView.TypeRepr$PrimitiveType and org.jetbrains.jps.builders.java.dependencyView.TypeRepr$ClassType are in unnamed module of loader java.net.URLClassLoader @2f2c9b19)。则清除一下idea的缓存,然后执行rebuild
  2. 若idea的terminal或者maven执行compile的时候报错未找到protoc命令,则可以在terminal中执行以下命令排查idea是否读取到最新的Path。若打印的Path值没有protoc的路径,则重启一下电脑。
shell 复制代码
# terminal中使用的是powershell的话执行
echo $env:path

# terminal中使用的是cmd的话执行
echo %path%

编译结果示例

java 复制代码
// Generated by the protocol buffer compiler.  DO NOT EDIT!
// NO CHECKED-IN PROTOBUF GENCODE
// source: person.proto
// Protobuf Java Version: 4.31.1

@com.google.protobuf.Generated
public final class PersonOuterClass {
  private PersonOuterClass() {}
  static {
    com.google.protobuf.RuntimeVersion.validateProtobufGencodeVersion(
      com.google.protobuf.RuntimeVersion.RuntimeDomain.PUBLIC,
      /* major= */ 4,
      /* minor= */ 31,
      /* patch= */ 1,
      /* suffix= */ "",
      PersonOuterClass.class.getName());
  }
  public static void registerAllExtensions(
      com.google.protobuf.ExtensionRegistryLite registry) {
  }

  public static void registerAllExtensions(
      com.google.protobuf.ExtensionRegistry registry) {
    registerAllExtensions(
        (com.google.protobuf.ExtensionRegistryLite) registry);
  }
  public interface PersonOrBuilder extends
      // @@protoc_insertion_point(interface_extends:Person)
      com.google.protobuf.MessageOrBuilder {

    /**
     * <code>string name = 1;</code>
     * @return The name.
     */
    java.lang.String getName();
    /**
     * <code>string name = 1;</code>
     * @return The bytes for name.
     */
    com.google.protobuf.ByteString
        getNameBytes();

    /**
     * <code>int32 id = 2;</code>
     * @return The id.
     */
    int getId();

    /**
     * <code>repeated string emails = 3;</code>
     * @return A list containing the emails.
     */
    java.util.List<java.lang.String>
        getEmailsList();
    /**
     * <code>repeated string emails = 3;</code>
     * @return The count of emails.
     */
    int getEmailsCount();
    /**
     * <code>repeated string emails = 3;</code>
     * @param index The index of the element to return.
     * @return The emails at the given index.
     */
    java.lang.String getEmails(int index);
    /**
     * <code>repeated string emails = 3;</code>
     * @param index The index of the value to return.
     * @return The bytes of the emails at the given index.
     */
    com.google.protobuf.ByteString
        getEmailsBytes(int index);
  }
  /**
   * Protobuf type {@code Person}
   */
  public static final class Person extends
      com.google.protobuf.GeneratedMessage implements
      // @@protoc_insertion_point(message_implements:Person)
      PersonOrBuilder {
  private static final long serialVersionUID = 0L;
    static {
      com.google.protobuf.RuntimeVersion.validateProtobufGencodeVersion(
        com.google.protobuf.RuntimeVersion.RuntimeDomain.PUBLIC,
        /* major= */ 4,
        /* minor= */ 31,
        /* patch= */ 1,
        /* suffix= */ "",
        Person.class.getName());
    }
    // Use Person.newBuilder() to construct.
    private Person(com.google.protobuf.GeneratedMessage.Builder<?> builder) {
      super(builder);
    }
    private Person() {
      name_ = "";
      emails_ =
          com.google.protobuf.LazyStringArrayList.emptyList();
    }

    public static final com.google.protobuf.Descriptors.Descriptor
        getDescriptor() {
      return PersonOuterClass.internal_static_Person_descriptor;
    }

    @java.lang.Override
    protected com.google.protobuf.GeneratedMessage.FieldAccessorTable
        internalGetFieldAccessorTable() {
      return PersonOuterClass.internal_static_Person_fieldAccessorTable
          .ensureFieldAccessorsInitialized(
              PersonOuterClass.Person.class, PersonOuterClass.Person.Builder.class);
    }

    public static final int NAME_FIELD_NUMBER = 1;
    @SuppressWarnings("serial")
    private volatile java.lang.Object name_ = "";
    /**
     * <code>string name = 1;</code>
     * @return The name.
     */
    @java.lang.Override
    public java.lang.String getName() {
      java.lang.Object ref = name_;
      if (ref instanceof java.lang.String) {
        return (java.lang.String) ref;
      } else {
        com.google.protobuf.ByteString bs = 
            (com.google.protobuf.ByteString) ref;
        java.lang.String s = bs.toStringUtf8();
        name_ = s;
        return s;
      }
    }
    /**
     * <code>string name = 1;</code>
     * @return The bytes for name.
     */
    @java.lang.Override
    public com.google.protobuf.ByteString
        getNameBytes() {
      java.lang.Object ref = name_;
      if (ref instanceof java.lang.String) {
        com.google.protobuf.ByteString b = 
            com.google.protobuf.ByteString.copyFromUtf8(
                (java.lang.String) ref);
        name_ = b;
        return b;
      } else {
        return (com.google.protobuf.ByteString) ref;
      }
    }

    public static final int ID_FIELD_NUMBER = 2;
    private int id_ = 0;
    /**
     * <code>int32 id = 2;</code>
     * @return The id.
     */
    @java.lang.Override
    public int getId() {
      return id_;
    }

    public static final int EMAILS_FIELD_NUMBER = 3;
    @SuppressWarnings("serial")
    private com.google.protobuf.LazyStringArrayList emails_ =
        com.google.protobuf.LazyStringArrayList.emptyList();
    /**
     * <code>repeated string emails = 3;</code>
     * @return A list containing the emails.
     */
    public com.google.protobuf.ProtocolStringList
        getEmailsList() {
      return emails_;
    }
    /**
     * <code>repeated string emails = 3;</code>
     * @return The count of emails.
     */
    public int getEmailsCount() {
      return emails_.size();
    }
    /**
     * <code>repeated string emails = 3;</code>
     * @param index The index of the element to return.
     * @return The emails at the given index.
     */
    public java.lang.String getEmails(int index) {
      return emails_.get(index);
    }
    /**
     * <code>repeated string emails = 3;</code>
     * @param index The index of the value to return.
     * @return The bytes of the emails at the given index.
     */
    public com.google.protobuf.ByteString
        getEmailsBytes(int index) {
      return emails_.getByteString(index);
    }

    private byte memoizedIsInitialized = -1;
    @java.lang.Override
    public final boolean isInitialized() {
      byte isInitialized = memoizedIsInitialized;
      if (isInitialized == 1) return true;
      if (isInitialized == 0) return false;

      memoizedIsInitialized = 1;
      return true;
    }

    @java.lang.Override
    public void writeTo(com.google.protobuf.CodedOutputStream output)
                        throws java.io.IOException {
      if (!com.google.protobuf.GeneratedMessage.isStringEmpty(name_)) {
        com.google.protobuf.GeneratedMessage.writeString(output, 1, name_);
      }
      if (id_ != 0) {
        output.writeInt32(2, id_);
      }
      for (int i = 0; i < emails_.size(); i++) {
        com.google.protobuf.GeneratedMessage.writeString(output, 3, emails_.getRaw(i));
      }
      getUnknownFields().writeTo(output);
    }

    @java.lang.Override
    public int getSerializedSize() {
      int size = memoizedSize;
      if (size != -1) return size;

      size = 0;
      if (!com.google.protobuf.GeneratedMessage.isStringEmpty(name_)) {
        size += com.google.protobuf.GeneratedMessage.computeStringSize(1, name_);
      }
      if (id_ != 0) {
        size += com.google.protobuf.CodedOutputStream
          .computeInt32Size(2, id_);
      }
      {
        int dataSize = 0;
        for (int i = 0; i < emails_.size(); i++) {
          dataSize += computeStringSizeNoTag(emails_.getRaw(i));
        }
        size += dataSize;
        size += 1 * getEmailsList().size();
      }
      size += getUnknownFields().getSerializedSize();
      memoizedSize = size;
      return size;
    }

    @java.lang.Override
    public boolean equals(final java.lang.Object obj) {
      if (obj == this) {
       return true;
      }
      if (!(obj instanceof PersonOuterClass.Person)) {
        return super.equals(obj);
      }
      PersonOuterClass.Person other = (PersonOuterClass.Person) obj;

      if (!getName()
          .equals(other.getName())) return false;
      if (getId()
          != other.getId()) return false;
      if (!getEmailsList()
          .equals(other.getEmailsList())) return false;
      if (!getUnknownFields().equals(other.getUnknownFields())) return false;
      return true;
    }

    @java.lang.Override
    public int hashCode() {
      if (memoizedHashCode != 0) {
        return memoizedHashCode;
      }
      int hash = 41;
      hash = (19 * hash) + getDescriptor().hashCode();
      hash = (37 * hash) + NAME_FIELD_NUMBER;
      hash = (53 * hash) + getName().hashCode();
      hash = (37 * hash) + ID_FIELD_NUMBER;
      hash = (53 * hash) + getId();
      if (getEmailsCount() > 0) {
        hash = (37 * hash) + EMAILS_FIELD_NUMBER;
        hash = (53 * hash) + getEmailsList().hashCode();
      }
      hash = (29 * hash) + getUnknownFields().hashCode();
      memoizedHashCode = hash;
      return hash;
    }

    public static PersonOuterClass.Person parseFrom(
        java.nio.ByteBuffer data)
        throws com.google.protobuf.InvalidProtocolBufferException {
      return PARSER.parseFrom(data);
    }
    public static PersonOuterClass.Person parseFrom(
        java.nio.ByteBuffer data,
        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
        throws com.google.protobuf.InvalidProtocolBufferException {
      return PARSER.parseFrom(data, extensionRegistry);
    }
    public static PersonOuterClass.Person parseFrom(
        com.google.protobuf.ByteString data)
        throws com.google.protobuf.InvalidProtocolBufferException {
      return PARSER.parseFrom(data);
    }
    public static PersonOuterClass.Person parseFrom(
        com.google.protobuf.ByteString data,
        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
        throws com.google.protobuf.InvalidProtocolBufferException {
      return PARSER.parseFrom(data, extensionRegistry);
    }
    public static PersonOuterClass.Person parseFrom(byte[] data)
        throws com.google.protobuf.InvalidProtocolBufferException {
      return PARSER.parseFrom(data);
    }
    public static PersonOuterClass.Person parseFrom(
        byte[] data,
        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
        throws com.google.protobuf.InvalidProtocolBufferException {
      return PARSER.parseFrom(data, extensionRegistry);
    }
    public static PersonOuterClass.Person parseFrom(java.io.InputStream input)
        throws java.io.IOException {
      return com.google.protobuf.GeneratedMessage
          .parseWithIOException(PARSER, input);
    }
    public static PersonOuterClass.Person parseFrom(
        java.io.InputStream input,
        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
        throws java.io.IOException {
      return com.google.protobuf.GeneratedMessage
          .parseWithIOException(PARSER, input, extensionRegistry);
    }

    public static PersonOuterClass.Person parseDelimitedFrom(java.io.InputStream input)
        throws java.io.IOException {
      return com.google.protobuf.GeneratedMessage
          .parseDelimitedWithIOException(PARSER, input);
    }

    public static PersonOuterClass.Person parseDelimitedFrom(
        java.io.InputStream input,
        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
        throws java.io.IOException {
      return com.google.protobuf.GeneratedMessage
          .parseDelimitedWithIOException(PARSER, input, extensionRegistry);
    }
    public static PersonOuterClass.Person parseFrom(
        com.google.protobuf.CodedInputStream input)
        throws java.io.IOException {
      return com.google.protobuf.GeneratedMessage
          .parseWithIOException(PARSER, input);
    }
    public static PersonOuterClass.Person parseFrom(
        com.google.protobuf.CodedInputStream input,
        com.google.protobuf.ExtensionRegistryLite extensionRegistry)
        throws java.io.IOException {
      return com.google.protobuf.GeneratedMessage
          .parseWithIOException(PARSER, input, extensionRegistry);
    }

    @java.lang.Override
    public Builder newBuilderForType() { return newBuilder(); }
    public static Builder newBuilder() {
      return DEFAULT_INSTANCE.toBuilder();
    }
    public static Builder newBuilder(PersonOuterClass.Person prototype) {
      return DEFAULT_INSTANCE.toBuilder().mergeFrom(prototype);
    }
    @java.lang.Override
    public Builder toBuilder() {
      return this == DEFAULT_INSTANCE
          ? new Builder() : new Builder().mergeFrom(this);
    }

    @java.lang.Override
    protected Builder newBuilderForType(
        com.google.protobuf.GeneratedMessage.BuilderParent parent) {
      Builder builder = new Builder(parent);
      return builder;
    }
    /**
     * Protobuf type {@code Person}
     */
    public static final class Builder extends
        com.google.protobuf.GeneratedMessage.Builder<Builder> implements
        // @@protoc_insertion_point(builder_implements:Person)
        PersonOuterClass.PersonOrBuilder {
      public static final com.google.protobuf.Descriptors.Descriptor
          getDescriptor() {
        return PersonOuterClass.internal_static_Person_descriptor;
      }

      @java.lang.Override
      protected com.google.protobuf.GeneratedMessage.FieldAccessorTable
          internalGetFieldAccessorTable() {
        return PersonOuterClass.internal_static_Person_fieldAccessorTable
            .ensureFieldAccessorsInitialized(
                PersonOuterClass.Person.class, PersonOuterClass.Person.Builder.class);
      }

      // Construct using PersonOuterClass.Person.newBuilder()
      private Builder() {

      }

      private Builder(
          com.google.protobuf.GeneratedMessage.BuilderParent parent) {
        super(parent);

      }
      @java.lang.Override
      public Builder clear() {
        super.clear();
        bitField0_ = 0;
        name_ = "";
        id_ = 0;
        emails_ =
            com.google.protobuf.LazyStringArrayList.emptyList();
        return this;
      }

      @java.lang.Override
      public com.google.protobuf.Descriptors.Descriptor
          getDescriptorForType() {
        return PersonOuterClass.internal_static_Person_descriptor;
      }

      @java.lang.Override
      public PersonOuterClass.Person getDefaultInstanceForType() {
        return PersonOuterClass.Person.getDefaultInstance();
      }

      @java.lang.Override
      public PersonOuterClass.Person build() {
        PersonOuterClass.Person result = buildPartial();
        if (!result.isInitialized()) {
          throw newUninitializedMessageException(result);
        }
        return result;
      }

      @java.lang.Override
      public PersonOuterClass.Person buildPartial() {
        PersonOuterClass.Person result = new PersonOuterClass.Person(this);
        if (bitField0_ != 0) { buildPartial0(result); }
        onBuilt();
        return result;
      }

      private void buildPartial0(PersonOuterClass.Person result) {
        int from_bitField0_ = bitField0_;
        if (((from_bitField0_ & 0x00000001) != 0)) {
          result.name_ = name_;
        }
        if (((from_bitField0_ & 0x00000002) != 0)) {
          result.id_ = id_;
        }
        if (((from_bitField0_ & 0x00000004) != 0)) {
          emails_.makeImmutable();
          result.emails_ = emails_;
        }
      }

      @java.lang.Override
      public Builder mergeFrom(com.google.protobuf.Message other) {
        if (other instanceof PersonOuterClass.Person) {
          return mergeFrom((PersonOuterClass.Person)other);
        } else {
          super.mergeFrom(other);
          return this;
        }
      }

      public Builder mergeFrom(PersonOuterClass.Person other) {
        if (other == PersonOuterClass.Person.getDefaultInstance()) return this;
        if (!other.getName().isEmpty()) {
          name_ = other.name_;
          bitField0_ |= 0x00000001;
          onChanged();
        }
        if (other.getId() != 0) {
          setId(other.getId());
        }
        if (!other.emails_.isEmpty()) {
          if (emails_.isEmpty()) {
            emails_ = other.emails_;
            bitField0_ |= 0x00000004;
          } else {
            ensureEmailsIsMutable();
            emails_.addAll(other.emails_);
          }
          onChanged();
        }
        this.mergeUnknownFields(other.getUnknownFields());
        onChanged();
        return this;
      }

      @java.lang.Override
      public final boolean isInitialized() {
        return true;
      }

      @java.lang.Override
      public Builder mergeFrom(
          com.google.protobuf.CodedInputStream input,
          com.google.protobuf.ExtensionRegistryLite extensionRegistry)
          throws java.io.IOException {
        if (extensionRegistry == null) {
          throw new java.lang.NullPointerException();
        }
        try {
          boolean done = false;
          while (!done) {
            int tag = input.readTag();
            switch (tag) {
              case 0:
                done = true;
                break;
              case 10: {
                name_ = input.readStringRequireUtf8();
                bitField0_ |= 0x00000001;
                break;
              } // case 10
              case 16: {
                id_ = input.readInt32();
                bitField0_ |= 0x00000002;
                break;
              } // case 16
              case 26: {
                java.lang.String s = input.readStringRequireUtf8();
                ensureEmailsIsMutable();
                emails_.add(s);
                break;
              } // case 26
              default: {
                if (!super.parseUnknownField(input, extensionRegistry, tag)) {
                  done = true; // was an endgroup tag
                }
                break;
              } // default:
            } // switch (tag)
          } // while (!done)
        } catch (com.google.protobuf.InvalidProtocolBufferException e) {
          throw e.unwrapIOException();
        } finally {
          onChanged();
        } // finally
        return this;
      }
      private int bitField0_;

      private java.lang.Object name_ = "";
      /**
       * <code>string name = 1;</code>
       * @return The name.
       */
      public java.lang.String getName() {
        java.lang.Object ref = name_;
        if (!(ref instanceof java.lang.String)) {
          com.google.protobuf.ByteString bs =
              (com.google.protobuf.ByteString) ref;
          java.lang.String s = bs.toStringUtf8();
          name_ = s;
          return s;
        } else {
          return (java.lang.String) ref;
        }
      }
      /**
       * <code>string name = 1;</code>
       * @return The bytes for name.
       */
      public com.google.protobuf.ByteString
          getNameBytes() {
        java.lang.Object ref = name_;
        if (ref instanceof String) {
          com.google.protobuf.ByteString b = 
              com.google.protobuf.ByteString.copyFromUtf8(
                  (java.lang.String) ref);
          name_ = b;
          return b;
        } else {
          return (com.google.protobuf.ByteString) ref;
        }
      }
      /**
       * <code>string name = 1;</code>
       * @param value The name to set.
       * @return This builder for chaining.
       */
      public Builder setName(
          java.lang.String value) {
        if (value == null) { throw new NullPointerException(); }
        name_ = value;
        bitField0_ |= 0x00000001;
        onChanged();
        return this;
      }
      /**
       * <code>string name = 1;</code>
       * @return This builder for chaining.
       */
      public Builder clearName() {
        name_ = getDefaultInstance().getName();
        bitField0_ = (bitField0_ & ~0x00000001);
        onChanged();
        return this;
      }
      /**
       * <code>string name = 1;</code>
       * @param value The bytes for name to set.
       * @return This builder for chaining.
       */
      public Builder setNameBytes(
          com.google.protobuf.ByteString value) {
        if (value == null) { throw new NullPointerException(); }
        checkByteStringIsUtf8(value);
        name_ = value;
        bitField0_ |= 0x00000001;
        onChanged();
        return this;
      }

      private int id_ ;
      /**
       * <code>int32 id = 2;</code>
       * @return The id.
       */
      @java.lang.Override
      public int getId() {
        return id_;
      }
      /**
       * <code>int32 id = 2;</code>
       * @param value The id to set.
       * @return This builder for chaining.
       */
      public Builder setId(int value) {

        id_ = value;
        bitField0_ |= 0x00000002;
        onChanged();
        return this;
      }
      /**
       * <code>int32 id = 2;</code>
       * @return This builder for chaining.
       */
      public Builder clearId() {
        bitField0_ = (bitField0_ & ~0x00000002);
        id_ = 0;
        onChanged();
        return this;
      }

      private com.google.protobuf.LazyStringArrayList emails_ =
          com.google.protobuf.LazyStringArrayList.emptyList();
      private void ensureEmailsIsMutable() {
        if (!emails_.isModifiable()) {
          emails_ = new com.google.protobuf.LazyStringArrayList(emails_);
        }
        bitField0_ |= 0x00000004;
      }
      /**
       * <code>repeated string emails = 3;</code>
       * @return A list containing the emails.
       */
      public com.google.protobuf.ProtocolStringList
          getEmailsList() {
        emails_.makeImmutable();
        return emails_;
      }
      /**
       * <code>repeated string emails = 3;</code>
       * @return The count of emails.
       */
      public int getEmailsCount() {
        return emails_.size();
      }
      /**
       * <code>repeated string emails = 3;</code>
       * @param index The index of the element to return.
       * @return The emails at the given index.
       */
      public java.lang.String getEmails(int index) {
        return emails_.get(index);
      }
      /**
       * <code>repeated string emails = 3;</code>
       * @param index The index of the value to return.
       * @return The bytes of the emails at the given index.
       */
      public com.google.protobuf.ByteString
          getEmailsBytes(int index) {
        return emails_.getByteString(index);
      }
      /**
       * <code>repeated string emails = 3;</code>
       * @param index The index to set the value at.
       * @param value The emails to set.
       * @return This builder for chaining.
       */
      public Builder setEmails(
          int index, java.lang.String value) {
        if (value == null) { throw new NullPointerException(); }
        ensureEmailsIsMutable();
        emails_.set(index, value);
        bitField0_ |= 0x00000004;
        onChanged();
        return this;
      }
      /**
       * <code>repeated string emails = 3;</code>
       * @param value The emails to add.
       * @return This builder for chaining.
       */
      public Builder addEmails(
          java.lang.String value) {
        if (value == null) { throw new NullPointerException(); }
        ensureEmailsIsMutable();
        emails_.add(value);
        bitField0_ |= 0x00000004;
        onChanged();
        return this;
      }
      /**
       * <code>repeated string emails = 3;</code>
       * @param values The emails to add.
       * @return This builder for chaining.
       */
      public Builder addAllEmails(
          java.lang.Iterable<java.lang.String> values) {
        ensureEmailsIsMutable();
        com.google.protobuf.AbstractMessageLite.Builder.addAll(
            values, emails_);
        bitField0_ |= 0x00000004;
        onChanged();
        return this;
      }
      /**
       * <code>repeated string emails = 3;</code>
       * @return This builder for chaining.
       */
      public Builder clearEmails() {
        emails_ =
          com.google.protobuf.LazyStringArrayList.emptyList();
        bitField0_ = (bitField0_ & ~0x00000004);;
        onChanged();
        return this;
      }
      /**
       * <code>repeated string emails = 3;</code>
       * @param value The bytes of the emails to add.
       * @return This builder for chaining.
       */
      public Builder addEmailsBytes(
          com.google.protobuf.ByteString value) {
        if (value == null) { throw new NullPointerException(); }
        checkByteStringIsUtf8(value);
        ensureEmailsIsMutable();
        emails_.add(value);
        bitField0_ |= 0x00000004;
        onChanged();
        return this;
      }

      // @@protoc_insertion_point(builder_scope:Person)
    }

    // @@protoc_insertion_point(class_scope:Person)
    private static final PersonOuterClass.Person DEFAULT_INSTANCE;
    static {
      DEFAULT_INSTANCE = new PersonOuterClass.Person();
    }

    public static PersonOuterClass.Person getDefaultInstance() {
      return DEFAULT_INSTANCE;
    }

    private static final com.google.protobuf.Parser<Person>
        PARSER = new com.google.protobuf.AbstractParser<Person>() {
      @java.lang.Override
      public Person parsePartialFrom(
          com.google.protobuf.CodedInputStream input,
          com.google.protobuf.ExtensionRegistryLite extensionRegistry)
          throws com.google.protobuf.InvalidProtocolBufferException {
        Builder builder = newBuilder();
        try {
          builder.mergeFrom(input, extensionRegistry);
        } catch (com.google.protobuf.InvalidProtocolBufferException e) {
          throw e.setUnfinishedMessage(builder.buildPartial());
        } catch (com.google.protobuf.UninitializedMessageException e) {
          throw e.asInvalidProtocolBufferException().setUnfinishedMessage(builder.buildPartial());
        } catch (java.io.IOException e) {
          throw new com.google.protobuf.InvalidProtocolBufferException(e)
              .setUnfinishedMessage(builder.buildPartial());
        }
        return builder.buildPartial();
      }
    };

    public static com.google.protobuf.Parser<Person> parser() {
      return PARSER;
    }

    @java.lang.Override
    public com.google.protobuf.Parser<Person> getParserForType() {
      return PARSER;
    }

    @java.lang.Override
    public PersonOuterClass.Person getDefaultInstanceForType() {
      return DEFAULT_INSTANCE;
    }

  }

  private static final com.google.protobuf.Descriptors.Descriptor
    internal_static_Person_descriptor;
  private static final 
    com.google.protobuf.GeneratedMessage.FieldAccessorTable
      internal_static_Person_fieldAccessorTable;

  public static com.google.protobuf.Descriptors.FileDescriptor
      getDescriptor() {
    return descriptor;
  }
  private static  com.google.protobuf.Descriptors.FileDescriptor
      descriptor;
  static {
    java.lang.String[] descriptorData = {
      "\n\014person.proto\"2\n\006Person\022\014\n\004name\030\001 \001(\t\022\n" +
      "\n\002id\030\002 \001(\005\022\016\n\006emails\030\003 \003(\tb\006proto3"
    };
    descriptor = com.google.protobuf.Descriptors.FileDescriptor
      .internalBuildGeneratedFileFrom(descriptorData,
        new com.google.protobuf.Descriptors.FileDescriptor[] {
        });
    internal_static_Person_descriptor =
      getDescriptor().getMessageTypes().get(0);
    internal_static_Person_fieldAccessorTable = new
      com.google.protobuf.GeneratedMessage.FieldAccessorTable(
        internal_static_Person_descriptor,
        new java.lang.String[] { "Name", "Id", "Emails", });
    descriptor.resolveAllFeaturesImmutable();
  }

  // @@protoc_insertion_point(outer_class_scope)
}

执行

从上面编译出来的文件中可以找到这行注释// Protobuf Java Version: 4.31.1,表示protobuf对应java依赖的版本。

  • 在pom.xml中引入依赖
xml 复制代码
<!-- Protobuf Java -->
<dependency>
  <groupId>com.google.protobuf</groupId>
  <artifactId>protobuf-java</artifactId>
  <version>4.31.1</version>
</dependency>
  • java测试代码
java 复制代码
public static void main(String[] args) throws InvalidProtocolBufferException {
    // 构造对象
    PersonOuterClass.Person person = PersonOuterClass.Person.newBuilder()
    .setName("Alice")
    .setId(123)
    .addEmails("alice@example.com")
    .build();

    // 序列化为字节数组
    byte[] bytes = person.toByteArray();

    // 反序列化
    PersonOuterClass.Person parsedPerson = PersonOuterClass.Person.parseFrom(bytes);
    System.out.println(parsedPerson.getName());
    System.out.println(parsedPerson.getId());
    System.out.println(parsedPerson.getEmails(0));

}
  • 执行结果
plain 复制代码
Alice
123
alice@example.com

性能测试

protobuf和json的性能对比

此处对比protobuf、hutool的json工具类、jackson的性能

  • 引入依赖
xml 复制代码
<!-- JUnit 5 -->
<dependency>
  <groupId>org.junit.jupiter</groupId>
  <artifactId>junit-jupiter-api</artifactId>
  <version>5.8.2</version>
</dependency>
<!-- jmh性能测试 -->
<dependency>
  <groupId>org.openjdk.jmh</groupId>
  <artifactId>jmh-core</artifactId>
  <version>1.37</version>
</dependency>
<!-- 编译时处理JMH的注解,若不加,编译出来的benchmark注解未被解析,运行时会报错 -->
<dependency>
  <groupId>org.openjdk.jmh</groupId>
  <artifactId>jmh-generator-annprocess</artifactId>
  <version>1.37</version>
  <scope>provided</scope>
</dependency>

<!-- jackson 依赖 -->
<dependency>
  <groupId>com.fasterxml.jackson.core</groupId>
  <artifactId>jackson-databind</artifactId>
  <version>2.15.2</version>
</dependency>
<dependency>
  <groupId>com.fasterxml.jackson.core</groupId>
  <artifactId>jackson-core</artifactId>
  <version>2.15.2</version>
</dependency>
<dependency>
  <groupId>com.fasterxml.jackson.core</groupId>
  <artifactId>jackson-annotations</artifactId>
  <version>2.15.2</version>
</dependency>

<!-- hutool 依赖 -->
<dependency>
  <groupId>cn.hutool</groupId>
  <artifactId>hutool-all</artifactId>
  <version>5.8.27</version>
</dependency>
  • 测试的pojo类
java 复制代码
public class PersonJson {

    private String name;
    private int id;
    private String[] emails;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String[] getEmails() {
        return emails;
    }

    public void setEmails(String[] emails) {
        this.emails = emails;
    }
}
  • 测试代码
java 复制代码
package protobuf;

import cn.hutool.json.JSONUtil;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.openjdk.jmh.annotations.*;

import java.io.IOException;

@Warmup(iterations = 2, time = 1)
@Measurement(iterations = 2, time = 1)
@Fork(2)
@State(Scope.Thread)
public class ProtobufBenchmark {
    private PersonOuterClass.Person person;
    private byte[] serializedData;
    private String jsonStr;

    private PersonJson personJson;

    private ObjectMapper objectMapper = new ObjectMapper();

    @Setup
    public void setup() {
        person = PersonOuterClass.Person.newBuilder()
                .setName("Charlie")
                .setId(789)
                .addEmails("charlie@example.com")
                .build();
        serializedData = person.toByteArray();

        personJson = new PersonJson();
        personJson.setName("Charlie");
        personJson.setId(789);
        personJson.setEmails(new String[]{"charlie@example.com"});

        jsonStr = JSONUtil.toJsonStr(personJson);
    }

    /**
     * 测试protobuf序列化
     *
     * @return
     */
    @Benchmark
    public byte[] testSerialize() {
        return person.toByteArray();
    }

    /**
     * 测试protobuf反序列化
     *
     * @return
     * @throws Exception
     */
    @Benchmark
    public PersonOuterClass.Person testDeserialize() throws Exception {
        return PersonOuterClass.Person.parseFrom(serializedData);
    }

    /**
     * 测试hutool-json序列化
     *
     * @return
     */
    @Benchmark
    public String testJsonSerialize() {
        return JSONUtil.toJsonStr(personJson);
    }

    /**
     * 测试hutool-json反序列化
     *
     * @return
     */
    @Benchmark
    public PersonJson testJsonDeserialize() {
        return JSONUtil.toBean(jsonStr, PersonJson.class);
    }

    /**
     * 测试Jackson序列化
     *
     * @return
     * @throws JsonProcessingException
     */
    @Benchmark
    public String testJacksonSerialize() throws JsonProcessingException {
        return objectMapper.writeValueAsString(personJson);
    }

    /**
     * 测试Jackson反序列化
     *
     * @return
     * @throws Exception
     */
    @Benchmark
    public PersonJson testJacksonDeserialize() throws Exception {
        return objectMapper.readValue(jsonStr, PersonJson.class);
    }

}

运行结果

plain 复制代码
Benchmark                                  Mode  Cnt         Score         Error  Units
ProtobufBenchmark.testSerialize           thrpt    4  23652174.844 ± 1801935.493  ops/s
ProtobufBenchmark.testDeserialize         thrpt    4   7830878.513 ± 2204277.060  ops/s
ProtobufBenchmark.testJacksonSerialize    thrpt    4   5737731.097 ±  975288.754  ops/s
ProtobufBenchmark.testJacksonDeserialize  thrpt    4   2806124.511 ±  396224.766  ops/s
ProtobufBenchmark.testJsonSerialize       thrpt    4    256885.571 ±   42109.112  ops/s
ProtobufBenchmark.testJsonDeserialize     thrpt    4    289602.156 ±   78181.194  ops/s
  • protobuf序列化性能是jackson的4倍,反序列化性能也将近3倍
  • hutool的json性能就比较差了,所以实际项目中若要使用json,推荐使用jackson

小结

本文介绍了protobuf从安装到使用的全过程,并提供了相应的代码示例。读者可以通过直接运行代码示例直观的学习到protobuf如何使用。最后测试和对比了protobuf、json序列化和反序列化的性能。

相关推荐
孫治AllenSun3 分钟前
【Mysql】联合索引生效分析案例
java·数据库·mysql
我命由我123455 分钟前
Spring Boot 项目问题:Web server failed to start. Port 5566 was already in use.
java·前端·jvm·spring boot·后端·spring·java-ee
书唐瑞6 分钟前
Percona pt-archiver 出现数据不对等
java·服务器·数据库
CHEN5_0223 分钟前
【Java面试题】缓存穿透
java·开发语言·数据库·redis·缓存
XMYX-033 分钟前
Java HTTPS 请求失败排查与证书导入全过程
java·https
北_鱼34 分钟前
设计模式1:创建型模式
java·设计模式·软件工程·代码规范·设计规范
惜鸟1 小时前
Mockito 的常见核心功能及注意事项
java·mockito
小毛驴8501 小时前
IntelliJ IDEA 的常用快捷键
java·ide·intellij-idea
搜狐技术产品小编20231 小时前
浅析责任链模式在视频审核场景中的应用
java·开发语言·责任链模式
泥泞开出花朵2 小时前
LRU缓存淘汰算法的详细介绍与具体实现
java·数据结构·后端·算法·缓存