Protobuf使用教程

protocol buffers介紹

protocol buffers 是一種語言無關、平臺無關、可擴展的序列化結構數據的方法,它可用於通信協議、數據存儲等。

protocol buffers優勢

protocol buffers 是一種靈活,高效,自動化機制的結構數據序列化方法-可類比 XML,但是比 XML 更小(3 ~ 10倍)、更快(20 ~ 100倍)、更爲簡單。你可以定義數據的結構,然後使用特殊生成的源代碼輕鬆的在各種數據流中使用各種語言進行編寫和讀取結構數據。你甚至可以更新數據結構,而不破壞根據舊數據結構編譯而成並且已部署的程序。
如何工作?

protocol buffers定義

你可以通過在 .proto 文件中定義 protocol buffer message 類型,來指定你想如何對序列化信息進行結構化。每一個 protocol buffer message 是一個信息的小邏輯記錄,包含了一系列的 name-value 對。這裏有一個非常基礎的 .proto 文件樣例,它定義了一個包含 “APE_Proto” 相關信息的 message,隨便定義了幾個字段:

syntax = "proto3";

option java_package = "com.test.protobuf";
option java_outer_classname = "Photo";

message Photo {
    double Longitude = 1;
    int64 Port = 2;
    string PlaceCode = 3;
    required int32 id = 4;
     optional string email = 5;
     repeated PhoneNumber phone = 6;
   
}

正如你所見,message 格式很簡單 - 每種 message 類型都有一個或多個具有唯一編號的字段,每個字段都有一個名稱和一個值類型,其中值類型可以是數字(整數或浮點數),布爾值,字符串,原始字節,甚至(如上例所示)其它 protocol buffer message 類型,這意味着允許你分層次地構建數據。你可以指定 optional 字段,required 字段和 repeated 字段。
option java_package是包名,他將在編譯後生成在option java_package指定的包名下
option java_outer_classname是生成的類名。

類型 string,名爲 stringVal 的 optional 可選字段,字段編號爲 1,此字段可出現 0 或 1 次
類型 bytes,名爲 bytesVal 的 optional 可選字段,字段編號爲 2,此字段可出現 0 或 1 次
類型 EmbeddedMessage(自定義的內嵌 message 類型),名爲 embeddedExample1 的 optional 可選字段,字段編號爲 3,此字段可出現 0 或 1 次
類型 int32,名爲 repeatedInt32Val 的 repeated 可重複字段,字段編號爲 4,此字段可出現 任意多次(包括 0)
類型 string,名爲 repeatedStringVal 的 repeated 可重複字段,字段編號爲 5,此字段可出現 任意多次(包括 0)

proto3 已捨棄 required 字段,optional 字段也無法顯示使用(因爲缺省默認就設置爲 optional)

protocol buffers 集成springboot使用idea

1,安裝插件
在這裏插入圖片描述

2,添加maven依賴

        <dependency>
            <groupId>io.protostuff</groupId>
            <artifactId>protostuff-runtime</artifactId>
            <version>1.4.0</version>
        </dependency>
        <dependency>
            <groupId>com.google.protobuf</groupId>
            <artifactId>protobuf-java</artifactId>
            <version>3.5.1</version>
        </dependency>
        <dependency>
            <groupId>io.protostuff</groupId>
            <artifactId>protostuff-core</artifactId>
            <version>1.4.0</version>
        </dependency>

3,添加插件

    <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.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
            <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>
                    <pluginId>grpc-java</pluginId>
                </configuration>
                <executions>
                    <execution>
                        <goals>
                            <goal>compile</goal>
                            <goal>compile-custom</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>

        </plugins>
    </build>

4,建立.proto文件
在這裏插入圖片描述
5,編寫message

syntax = "proto3";

option java_package = "com.test.demo";//包名
option java_outer_classname = "MessageBuf";//類名


message Message {
    string msg = 1; //消息內容
    int64 type = 2; //消息類型
    double lon = 3; //經度
    double lat = 4; //緯度

}

6,編譯生成Java文件
第一步:
在這裏插入圖片描述
第二步複製此java文件到指定包下使用:
在這裏插入圖片描述
複製到此處的包名要與上面包名一致,否則就要修改。
在這裏插入圖片描述
7,寫我們要用的java本地對象

package com.demo.protobuf;

import lombok.Data;

import java.io.Serializable;

/**
 * @author LiHaitao
 * @description Message:
 * @date 2019/11/1 15:40
 **/
@Data
public class Message implements Serializable {

    private String msg; //消息內容
    private Integer type; //消息類型
    private double lon; //經度
    private double lat; //緯度
}

8,protobuf工具類

package com.demo.protobuf;

import io.protostuff.LinkedBuffer;
import io.protostuff.ProtostuffIOUtil;
import io.protostuff.Schema;
import io.protostuff.runtime.RuntimeSchema;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.objenesis.Objenesis;
import org.springframework.objenesis.ObjenesisStd;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * ProtoBufUtil 轉換工具類
 *
 */
public class ProtoBufUtil {
    private static Logger log = LoggerFactory.getLogger(ProtoBufUtil.class);
    private static Map<Class<?>, Schema<?>> cachedSchema = new ConcurrentHashMap<Class<?>, Schema<?>>();

    private static Objenesis objenesis = new ObjenesisStd(true);

    @SuppressWarnings("unchecked")
    private static <T> Schema<T> getSchema(Class<T> cls) {
        Schema<T> schema = (Schema<T>) cachedSchema.get(cls);
        if (schema == null) {
            schema = RuntimeSchema.createFrom(cls);
            if (schema != null) {
                cachedSchema.put(cls, schema);
            }
        }
        return schema;
    }

    public ProtoBufUtil() {
    }

    @SuppressWarnings({"unchecked"})
    public static <T> byte[] serializer(T obj) {
        Class<T> cls = (Class<T>) obj.getClass();
        LinkedBuffer buffer = LinkedBuffer.allocate(LinkedBuffer.DEFAULT_BUFFER_SIZE);
        try {
            Schema<T> schema = getSchema(cls);
            return ProtostuffIOUtil.toByteArray(obj, schema, buffer);
        } catch (Exception e) {
            log.error("protobuf序列化失敗");
            throw new IllegalStateException(e.getMessage(), e);
        } finally {
            buffer.clear();
        }
    }

    public static <T> T deserializer(byte[] bytes, Class<T> clazz) {
        try {
            T message = (T) objenesis.newInstance(clazz);
            Schema<T> schema = getSchema(clazz);
            ProtostuffIOUtil.mergeFrom(bytes, message, schema);
            return message;
        } catch (Exception e) {
            log.error("protobuf反序列化失敗");
            throw new IllegalStateException(e.getMessage(), e);
        }
    }

}

9,使用練習

package com.demo.protobuf;

import com.google.protobuf.InvalidProtocolBufferException;

/**
 * @author LiHaitao
 * @description Test:
 * @date 2019/10/31 19:37
 **/
public class Test {
    /**
     * message xxx {
     * // 字段規則:required -> 字段只能也必須出現 1 次
     * // 字段規則:optional -> 字段可出現 0 次或1次
     * // 字段規則:repeated -> 字段可出現任意多次(包括 0)
     * // 類型:int32、int64、sint32、sint64、string、32-bit ....
     * // 字段編號:0 ~ 536870911(除去 19000 到 19999 之間的數字)
     * 字段規則 類型 名稱 = 字段編號;
     * }
     *
     * @param args
     */
    public static void main(String[] args) throws InvalidProtocolBufferException {
        /**
         * 創建.proto文件,定義數據結構
         */
        Message message = new Message();
        message.setLat(12.22);
        message.setLon(23.22);
        message.setMsg("你好!");
        message.setType(1L);
        //序列化message對象,進行傳輸使用
        byte[] serializer = ProtoBufUtil.serializer(message);
        //通過解析字節,轉化爲buf對象
        MessageBuf.Message messageBuf = MessageBuf.Message.parseFrom(serializer);
        //再通過buf對象轉化成使用對象message
        Message messageNow = new Message();
        messageNow.setType(messageBuf.getType());
        messageNow.setMsg(messageBuf.getMsg());
        messageNow.setLon(messageBuf.getLon());
        messageNow.setLat(messageBuf.getLat());
        System.out.println(messageNow);
        //直接利用protobuf的工具類反序列化
        Message deserializer = ProtoBufUtil.deserializer(serializer, Message.class);
        System.out.println(deserializer);

    }
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章