首先Google的protobuf是一个rpc框架。

什么是rpc呢?

rpc全称Remote Procedure Call,远程过程调用,远程是相对于本地而言。本地:直接new一个对象后调用方法;远程:接收传输数据再序列化得到对象,再用此对象调用对应的方法。

rpc可以拿来做什么捏~( ̄▽ ̄)~*

  • 解决分布式系统中,服务之间的调用问题(各功能之间的数据传递)
  • 远程调用时,要能够像本地调用一样方便,让调用者感知不到远程调用的逻辑(服务器间的数据请求操作等)

更详细地说明一下rpc的远程调用

  • 本地函数的调用
    如果是在一个程序中调用函数,就直接新建一个Calculator对象,Calculator类中定义了add函数,再调用由CalculatorImpl实现的add方法。这种就是本地函数的调用
    本地调用

  • 远程函数的调用
    基于高性能等方面的考虑,需要将系统改造成分布式系统,将功能单独放在不同的服务器中,调用的时候就访问不同的服务器进行调用。如下示例,Service A中没有CalculatorImpl类,就需要调用service B暴露的接口进行调用。
    两个服务器间数据的传递就需要采用各种协议,如果把这些操作进行封装,用户只需要像调用本地函数一样调用远程函数,这个就很RPC
    远程调用

Google Protocol Buffers介绍

Protocol Buffers简称protobuf,是Google推出与语言无关,平台无关的可扩展机制,用于序列化结构化数据。简单而言就是一个进行数据编解码的框架,可以看前一篇的介绍 -- 为什么要使用Protocol Buffers捏

Protobuf 安装

protobuf for java readme文件

protoc编译器安装 -- 下载地址

编译器只针对电脑系统而言(window、mac、linux),在assets下载文件的最后,选择对应的平台下载,这里下载protoc-3.7.1-win64.zip,解压后将bin目录加入环境变量。 -- 打开控制台输入protoc看看

项目中使用protobuf -- java语言

需要导入两个包protobuf-java和protobuf-java-util,注意使用的版本需要和上面下载使用的protoc编译器版本一样,否则会报错 -- (会报什么错捏,试试就知道)

//gradle 包配置
dependencies {
    //testCompile group: 'junit', name: 'junit', version: '4.12'
    compile (
            "io.netty:netty-all:4.1.10.Final",
            "com.google.protobuf:protobuf-java:3.7.1",
            "com.google.protobuf:protobuf-java-util:3.7.1",
    )
}

使用Protobuf,开始编码

前面说了一大堆,此时到了最重要的部分bb

由proto文件生成类示例

新建一个gradle/meven/普通java项目,导入上面两个包,在src下建一个protobuf目录保存proto文件
建个Student.proto和AdressBook.proto文件。(当然了,两个文件之间是没有联系的,纯粹是因为一个简单点,一个复杂点)

层次目录

Student.proto

syntax = "proto2";
package cn.shafish.protobuf;

 option optimize_for = SPEED;
 option java_package = "cn.shafish.protobuf";
 option java_outer_classname = "DataInfo";

 message Student {
     required string name = 1;
     optional int32 age = 2;
     optional string address = 3;
 }

AdressBook.proto

syntax = "proto3";

package tutorial;

option java_package = "cn.shafish.protobuf";
option java_outer_classname = "AddressBookProtos";

message Person {
  string name = 1;
  int32 id = 2;
  string email = 3;

  enum PhoneType {
    MOBILE = 0;
    HOME = 1;
    WORK = 2;
  }

  message PhoneNumber {
    string number = 1;
    PhoneType type = 2;
  }

  repeated PhoneNumber phones = 4;
}

message AddressBook {
  repeated Person people = 1;
}

可以看到这两个proto文件分别是用proto2和proto3的,就示例代码而言,最大的区别是proto3的枚举数据tag num以0开始,而proto2中是以1开始的 -- 可以搜搜两者的差异点。此外定义了java_package和java_outer_classname,.proto文件的编码格式可以参考上一篇文章介绍 -- Protocol格式定义,proto2,proto3
定义完.proto文件后,再使用在上一篇文章提到的 -- protoc编译器使用方法 ,代码如下:

protoc --java_out=src/main/java src/protobuf/AdressBook.proto
protoc --java_out=src/main/java src/protobuf/Student.proto
// 可以不用写 -I=src/protobuf

生成类文件
【图点击放大】

由结果就可以看见java_package 指定的是类的路径,java_outer_classname 指定的是类名

生成的类使用

public class ProtobufTest {
    public static void main(String[] args) throws Exception{
        //使用builders 设置对象值
        DataInfo.Student student = DataInfo.Student.newBuilder().
                setName("张三").setAge(20).setAddress("珠海").build();
        //将对象转为二进制数据
        byte[] studentByte = student.toByteArray();

        //将二进制数据进行不同服务器端的传递,就可以将对象进行反序列化使用

        //从二进制数据中取出对象使用
        DataInfo.Student student2 = DataInfo.Student.parseFrom(studentByte);
        System.out.println(student2.getAddress());
        System.out.println(student2.getAge());
        System.out.println(student2.getName());
    }
}