## 什么是protobuf
Protobuf(Protocol Buffer)是google 的一种数据交换的格式,它独立于语言,独立于平台。google 提供了多种语言的实现:java、c#、c++、go 和 python,每一种实现都包含了相应语言的编译器以及库文件。由于它是一种二进制的格式,比使用 xml 进行数据交换快许多。可以把它用于分布式应用之间的数据通信或者异构环境下的数据交换。作为一种效率和兼容性都很优秀的二进制数据传输格式,可以用于诸如网络传输、配置文件、数据存储等诸多领域。(参考[链接](https://baike.baidu.com/item/protocol%20buffer/1664400?fr=aladdin))
## 什么是protoc
protoc是protobuf文件(.proto)的编译器(参考[链接](https://github.com/google/protobuf)),可以借助这个工具把 .proto 文件转译成各种编程语言对应的源码,包含数据类型定义、调用接口等。
![protoc编译过程示意图](https://static.studygolang.com/180325/41775c725a2745e2e785f22bbd96a08c.png)
通过查看protoc的源码(参见[github库](https://github.com/google/protobuf))可以知道,protoc在设计上把protobuf和不同的语言解耦了,底层用c++来实现protobuf结构的存储,然后通过插件的形式来生成不同语言的源码。可以把protoc的编译过程分成简单的两个步骤(如上图所示):1)解析.proto文件,转译成protobuf的原生数据结构在内存中保存;2)把protobuf相关的数据结构传递给相应语言的编译插件,由插件负责根据接收到的protobuf原生结构渲染输出特定语言的模板。
源码中(参见[github库](https://github.com/google/protobuf/blob/master/src/google/protobuf/compiler/main.cc))包含的插件有 csharp、java、js、objectivec、php、python、ruby等多种。
## 什么是protoc-gen-go
protoc-gen-go是protobuf**编译插件**系列中的Go版本。从上一小节知道原生的protoc并不包含Go版本的插件,不过可以在github上发现专门的代码库(参见[github库](github.com/golang/protobuf/protoc-gen-go))。
由于protoc-gen-go是Go写的,所以安装它变得很简单,只需要运行
```go get -u github.com/golang/protobuf/protoc-gen-go```,便可以在$GOPATH/bin目录下发现这个工具。至此,就可以通过下面的命令来使用protoc-gen-go了。
```shell
protoc --go_out=output_directory input_directory/file.proto
```
其中"--go_out="表示生成Go文件,protoc会自动寻找PATH(系统执行路径)中的protoc-gen-go执行文件。
## protoc-gen-go的源码
![protoc-gen-go的源码结构示意图](https://static.studygolang.com/180325/fcfa10f6aa545027a3904783f0fd6ab5.png)
按照Go的代码风格,protoc-gen-go源码主要包含六个包(package):
* main包
* doc.go 主要是说明。
* link_grpc.go 显式引用protoc-gen-go/grpc包,触发grpc的init函数。
* main.go 代码不到50行,初始化generator,并调用generator相应的方法输出protobuf的Go语言文件。
* generator包
* generator.go 包含了大部分由protobuf原生结构到Go语言文件的渲染方法,其中 `func (g *Generator) P(str ...interface{})` 这个方法会把渲染输出到generator的output(generator匿名嵌套了bytes.Buffer,因此有Buffer的方法)。
* name_test.go 测试,主要包含generator中名称相关方法的测试。
* grpc包
* grpc.go 与generator相似,但是包含了很多生成grpc相关方法的方法,比如渲染转译protobuf中定义的rpc方法(在generator中不包含,其默认不转译service的定义)
* descriptor 包含protobuf的描述文件(.proto文件及其对应的Go编译文件),其中proto文件来自于proto库(参见[这里](https://github.com/google/protobuf/blob/master/src/google/protobuf/descriptor.proto))
* plugin 包含plugin的描述文件(.proto文件及其对应的Go编译文件),其中proto文件来自于proto库,参见[这里](https://github.com/google/protobuf/blob/master/src/google/protobuf/compiler/plugin.proto)
## 结语
从巴别塔的传说(参见[这里](https://baike.baidu.com/item/%E5%B7%B4%E5%88%AB%E5%A1%94/67557?fr=aladdin))可以知道,欲要构建大系统,个体之间的沟通规范很重要。protobuf的出现,为不同系统之间的连接提供了一种语言规范,只要遵循了这个规范,各个系统之间就是解耦的,非常适合近年来流行的微服务架构。
如果吧protoc和protoc-gen-go看成两个微服务,可以发现这两个服务就是完全解耦的;两者完全负责不同的功能,可以分别编码、升级,串接这两个服务的就是proto规范。
有疑问加站长微信联系(非本文作者))