高效编解码协议之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

验证

# 打开cmd命令行,执行以下命令 protoc --version  # 输出结果如下,表示安装成功 libprotoc 31.1 

定义数据结构

  • 新建person.proto文件
// person.proto syntax = "proto3";  message Person {   string name = 1;   int32 id = 2;   repeated string emails = 3; } 

编译

手动执行命令

# 打开cmd命令行,切换到person.proto文件目录下,执行以下命令 protoc --java_out=./ ./person.proto  # 当前目录下会生成PersonOuterClass.java文件 # 将PersonOuterClass.java复制到对应项目目录就可以使用了 # PersonOuterClass.java默认没有package路径,需要手动加一下  

maven插件编译

  • 在pom.xml中加入以下代码,执行maven的clean compile
<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>xxxbinprotoc.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的路径,则重启一下电脑。
# terminal中使用的是powershell的话执行 echo $env:path  # terminal中使用的是cmd的话执行 echo %path% 

编译结果示例

// 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 = {       "n14person.proto"2n06Person2214n04name3001 01(t22n" +       "n02id3002 01(052216n06emails3003 03(tb06proto3"     };     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中引入依赖
<!-- Protobuf Java --> <dependency>   <groupId>com.google.protobuf</groupId>   <artifactId>protobuf-java</artifactId>   <version>4.31.1</version> </dependency> 
  • 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));  } 
  • 执行结果
Alice 123 alice@example.com 

性能测试

protobuf和json的性能对比

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

  • 引入依赖
<!-- 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类
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;     } }  
  • 测试代码
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);     }  } 

运行结果

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序列化和反序列化的性能。

发表评论

评论已关闭。

相关文章