# grpc远程调用
**Repository Path**: ycfrank/grpc-study
## Basic Information
- **Project Name**: grpc远程调用
- **Description**: 介绍 grpc 的使用,从 简单代码,逐步集成,介绍普通 调用 及文件下载支持
- **Primary Language**: Java
- **License**: Not specified
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 0
- **Created**: 2024-01-20
- **Last Updated**: 2024-01-25
## Categories & Tags
**Categories**: Uncategorized
**Tags**: Grpc
## README
### gRPC简介
gRPC是谷歌开源的基于go语言的一个现代的开源高性能RPC框架,可以在任何环境中运行。它可以有效地连接数据中心内和跨数据中心的服务,并提供可插拔的支持,以实现负载平衡,跟踪,健康检查和身份验证。它还适用于分布式计算的最后一英里,用于将设备,移动应用程序和浏览器连接到后端服务。
gRPC 是一个高性能、开源和通用的 RPC 框架,面向移动和 HTTP/2 设计。目前提供 C、Java 和 Go 语言版本,分别是:grpc, grpc-java(git clone
https://github.com/grpc/grpc-java.git), grpc-go. 其中 C 版本支持 C, C++, Node.js, Python, Ruby, Objective-C, PHP 和 C# 支持.
相比于 Dubbo 构建于 TPC 传输层之上,gRPC 基于 HTTP/2 标准设计,带来诸如双向流、流控、头部压缩、单 TCP 连接上的多复用请求等特。这些特性使得其在移动设备上表现更好,更省电和节省空间占用。
- Coverage & Simplicity,协议设计和框架实现要足够通用和简单,能运行在任何设备之上,甚至一些资源首先的如 IoT、Mobile 等设备。
- Interoperability & Reach,要构建在更通用的协议之上,协议本身要能被网络上几乎所有的基础设施所支持。
- General Purpose & Performant,要在场景和性能间做好平衡,首先协议本身要是适用于各种场景的,同时也要尽量有高的性能。
- Payload Agnostic,协议上传输的负载要保持语言和平台中立。
- Streaming,要支持 Request - Response、Request - Stream、Bi-Steam 等通信模型。
- Flow Control,协议自身具备流量感知和限制的能力。
- Metadata Exchange,在 RPC 服务定义之外,提供额外附加数据传输的能力。
总的来说,在这样的设计理念指导下,gRPC 最终被设计为一个跨语言、跨平台的、通用的、高性能的、基于 HTTP/2 的 RPC 协议和框架。
与许多 RPC 系统类似,gRPC 也是基于以下理念:定义一个服务,指定其能够被远程调用的方法(包含参数和返回类型)。gRPC 客户端和服务端可以在多种环境中运行和交互 - 从 google 内部的服务器到你自己的笔记本,并且可以用任何
gRPC 支持的语言来编写。
gRPC 框架自身的弊端在于易用性不足以及服务治理能力欠缺(这也是目前绝大多数厂商不会直接裸用 gRPC 框架的原因)
需要注意的是,并不是所有 gRPC 支持的语言都可以编写我们例子的服务端代码,比如 PHP 和 Objective-C 仅支持创建客户端。
1.1 传统rpc对比Grpc
- 传统RPC框架:使用各种协议和编码方式进行通信,可能导致跨语言通信困难、性能不佳等问题;
- GRPC框架:使用Google开发的ProtoBuf进行序列化、并通过http/2(多路复用)进行通信,通信效率更加高效;
1.2 GRPC的优势
- 高性能:使用二进制的ProtoBuf编码和http/2多路复用机制等技术,实现低延迟和高吞吐的通信;
- 支持多语言:C++、Java、Python、Go等等;
- 强类型:使用ProtoBuf定义消息和接口,消除了手动解析数据的麻烦;
- 双向流式通信:GRPC支持双向流式传输,适用于实时性要求高的场景;
- 自动代码生成:根据定义好的服务接口和消息,GRPC自动生成客户端和服务端的代码,简化开发流程;
1.3 GRPC在分布式系统中的应用场景
- 微服务通信;
- 跨数据中心通信;
- 实时通信;
### proto3
Protocol Buffers(Protobuf)是 Google 推出的一个跨平台、语言中立的结构化数据描述和序列化的产品,它定义了一套结构化数据定义的协议,同时也提供了相应的 Compiler
工具,用来将语言中立的描述转化为相应语言的具体描述。
它的一些特性包括:
- 跨语言 跨平台,语言中立的数据描述格式,默认提供了生成多种语言的 Compiler 工具。
- 安全性,由于反序列化的范围和输出内容格式都是 Compiler 在编译时预生成的,因此绕过了类似 Java Deserialization Vulnarability 的问题。
- 二进制 高性能
- 强类型
- 字段变更向后兼容
除了结构化数据描述之外,Protobuf 还支持定义 RPC 服务,它允许我们定义一个 .proto 的服务描述文件,进而利用 Protobuf Compiler 工具生成特定语言和 RPC 框架的接口和 stub。后续将要具体讲到的
gRPC + Protobuf、Dubbo-gRPC + Protobuf 以及 Dubbo + Protobuf 都是通过定制 Compiler 类实现的。
也就是说,我在ubuntu下用python语言序列化一个对象,并使用http协议传输到使用java语言的android客户端,java使用对用的代码工具进行反序列化,也可以得到对应的对象。可以根据客户端与服务端约定同一个Proto文件进行交互,然后双方可以根据proto文件反序列化为相应语言的对象进行交互。
gRPC 默认使用 protocol buffers,这是 Google 开源的一套成熟的结构数据序列化机制(当然也可以使用其他数据格式如 JSON)。
尽管 protocol buffers 对于开源用户来说已经存在了一段时间,例子内使用的却一种名叫 proto3 的新风格的 protocol buffers,它拥有轻量简化的语法、一些有用的新功能,并且支持更多新语言。
虽然你可以使用 proto2 (当前默认的 protocol buffers 版本), 我们通常建议你在 gRPC 里使用 proto3,因为这样你可以使用 gRPC 支持全部范围的的语言,并且能避免 proto2 客户端与 proto3
服务端交互时出现的兼容性问题,反之亦然。
gRPC 提供 protocol buffer 编译插件,能够从一个服务定义的 .proto 文件生成客户端和服务端代码。
**protobuf中的数据类型限定修饰符:**
protobuf 2 中有三种数据类型限定修饰符: required, optional, repeated required表示字段必选,optional表示字段可选,repeated表示一个数组类型。 其中, required 和
optional 已在 proto3 弃用了。
**protobuf中常用的数据类型:**
- bool, 布尔类型
- double, 64位浮点数
- float, 32位浮点数
- int32, 32位整数
- int64, 64位整数
- uint64, 64位无符号整数
- sint32, 32位整数,处理负数效率更高
- sint64, 64位整数,处理负数效率更高
- string, 只能处理ASCII字符
- bytes, 用于处理多字节的语言字符
- enum, 枚举类型
**gRPC 允许你定义四类服务方法:**
- **单项 RPC**,即客户端发送一个请求给服务端,从服务端获取一个应答,就像一次普通的函数调用。
```protobuf
rpc SayHello(HelloRequest) returns (HelloResponse){}
```
- 一旦客户端通过桩调用一个方法,服务端会得到相关通知 ,通知包括客户端的元数据,方法名,允许的响应期限(如果可以的话)
- 服务端既可以在任何响应之前直接发送回初始的元数据,也可以等待客户端的请求信息,到底哪个先发生,取决于具体的应用。
- 一旦服务端获得客户端的请求信息,就会做所需的任何工作来创建或组装对应的响应。如果成功的话,这个响应会和包含状态码以及可选的状态信息等状态明细及可选的追踪信息返回给客户端 。
- 假如状态是 OK 的话,客户端会得到应答,这将结束客户端的调用。
- **服务端流式 RPC**,即客户端发送一个请求给服务端,可获取一个数据流用来读取一系列消息。客户端从返回的数据流里一直读取直到没有更多消息为止。
```protobuf
rpc LotsOfReplies(HelloRequest) returns (stream HelloResponse){ }
```
服务端流式 RPC 除了在得到客户端请求信息后发送回一个应答流之外,与我们的简单例子一样。
- **客户端流式 RPC**,即客户端用提供的一个数据流写入并发送一系列消息给服务端。一旦客户端完成消息写入,就等待服务端读取这些消息并返回应答。
```protobuf
rpc LotsOfGreetings(stream HelloRequest) returns (HelloResponse) { }
```
客户端流式 RPC 也基本与我们的简单例子一样,区别在于客户端通过发送一个请求流给服务端,取代了原先发送的单个请求。服务端通常(但并不必须)会在接收到客户端所有的请求后发送回一个应答,其中附带有它的状态详情和可选的跟踪数据。
- **双向流式 RPC**
,即两边都可以分别通过一个读写数据流来发送一系列消息。这两个数据流操作是相互独立的,所以客户端和服务端能按其希望的任意顺序读写,例如:服务端可以在写应答前等待所有的客户端消息,或者它可以先读一个消息再写一个消息,或者是读写相结合的其他方式。每个数据流里消息的顺序会被保持。
```protobuf
rpc BidiHello(stream HelloRequest) returns (stream HelloResponse){ }
```
双向流式 RPC ,调用由客户端调用方法来初始化,而服务端则接收到客户端的元数据,方法名和截止时间。服务端可以选择发送回它的初始元数据或等待客户端发送请求。 下一步怎样发展取决于应用,因为客户端和服务端能在任意顺序上读写 -
这些流的操作是完全独立的。例如服务端可以一直等直到它接收到所有客户端的消息才写应答,或者服务端和客户端可以像"乒乓球"一样:服务端后得到一个请求就回送一个应答,接着客户端根据应答来发送另一个请求,以此类推。
**优点:**
① 性能高效:
与XML相比,protobuf更小(3 ~ 10倍)、更快(20 ~ 100倍)、更为简单。
② 语言无关、平台无关:
protobuf支持Java、C++、Python等多种语言,支持多个平台。
③ 扩展性、兼容性强:
只需要使用protobuf对结构数据进行一次描述,即可从各种数据流中读取结构数据,更新数据结构时不会破坏原有的程序。
**缺点:**
① 自解释性较差,数据存储格式为二进制,需要通过 .proto 文件才能了解到内部的数据结构;
② 不适合用来对 基于文本的标记文档(如HTML) 建模。
> 在 gRPC 里,客户端和服务端对调用成功的判断是独立的、本地的,他们的结论可能不一致。这意味着,比如你有一个 RPC 在服务端成功结束("我已经返回了所有应答!"),到那时在客户端可能是失败的("应答在最后期限后才来到!")。也可能在客户端把所有请求发送完前,服务端却判断调用已经完成了。
### 项目创建
在我们开始添加依赖关系之前,让我们项目的一些设置建议开始。

我们建议将您的项目分为2至3个不同的模块。
- **interface 项目** 包含原始 protobuf 文件并生成 java model 和 service 类。 你可能会在不同的项目中会共享这个部分。
- **Server 项目** 包含项目的业务实现,并使用上面的 Interface 项目作为依赖项。
- **Client 项目**(可选,可能很多) 任何使用预生成的 stub 来访问服务器的客户端项目。
### 定义服务:api
创建我们例子的第一步是定义一个服务:一个 RPC 服务通过参数和返回类型来指定可以远程调用的方法。
我们使用 protocol buffers 接口定义语言来定义服务方法,用 protocol buffer 来定义参数和返回类型。客户端和服务端均使用服务定义生成的接口代码。
1、编写proto文件,我这里是在api中编写,跟java直接使用grpc一样,也需要注意文件存放的位置(将你的proto文件放到src/main/proto和src/test/proto目录)。
```protobuf
syntax = "proto3";//标识 proto版本 建议使用proto3
//package userinfoservice;//proto包名 避免命名冲突,也可以作为引入其他proto文件时使用
option java_multiple_files = true;
option java_package = "io.grpc.examples";//生成的类将带有此包名,不指定则使用package
service UserInfoService {
rpc queryUserInfo(UserInfoReq) returns (UserInfoResponse) {}
rpc queryUserInfo2(UserInfoReq) returns (UserInfoResponse) {}
rpc queryUserInfo3(UserStr) returns (UserStr) {}
}
message UserStr{
string str = 1;
}
message UserInfoReq {
string name = 1;
int64 id = 2;
}
message UserInfoResponse {
int32 code = 1;
string msg = 2;
bool success = 3;
message Data {
UserInfo userInfo = 1;
}
Data data = 4;
}
message UserInfo {
int64 id = 1;
string name = 2;
string sex = 3;
string addr = 4;
}
```
每个元素上的“= 1”,“=
2”标记是标识该字段在二进制编码中的唯一“标记”。标记号1-15相对于比较大的数字只需要一个字节进行编码,因此作为优化,可以决定将这些标记号用于常用或重复的元素,将标记号16和更高的标记号留给不太常用的可选元素。重复字段中的每个元素都需要重新编码标记号,因此重复字段特别适合此优化。
在 `.proto` 文件中我们指定了一个 java_package 文件的选项,如果在 .proto 文件中没有显示的 java_package 参数, 那么就会使用缺省的 proto 包(通过 "package" 关键字指定)。
但是,因为 proto 包一般不是以域名 翻转的格式命名,所以它不是好的 Java 包。 如果我们用其它语言通过 .proto 文件生成代码,java_package 是不起任何作用的。
在Python中,包通常由目录结构决定,因此package在.proto文件中定义的内容对生成的代码没有影响。但是,仍应声明一个以避免在protobuf名称空间以及非Python语言中发生名称冲突。
要定义一个服务,你必须在你的 .proto 文件中指定 service, 然后在我们的服务中定义 rpc 方法,指定它们的请求的和响应类型。
UserInfoService 服务有三个方法,可以让服务端从远程客户端接收一个包含用户信息的 UserInfoReq 消息后,在一个 queryUserInfo 里发送回一个 UserInfoService
一旦定义好服务,我们可以使用 protocol buffer 编译器 protoc 来生成创建应用所需的特定客户端和服务端的代码 - 你可以生成任意 gRPC 支持的语言的代码,当然 PHP 和 Objective-C
仅支持创建客户端代码。生成的代码同时包括客户端的存根和服务端要实现的抽象接口,均包含 UserInfoService 所定义的方法。
> 在 Protobuf 的定义中,二进制流可以使用 bytes类型来表示
> 假设我们这里不考虑多个文件的上传,那么,我们有两种策略来处理 二进制流 这个参数。方案一是直接把整个文件取出来。然后一次性发出去;方案二则是把整个文件分成不同的块,然后按照顺序逐个发出去。
2、在api的pom.xml中添加编译依赖,这里需要注意版本(protoc:3.12.0),其它的根据这个进行适配,这里没什么变化,java直接使用grpc一致
```xml
kr.motd.maven
os-maven-plugin
1.6.2
org.xolstice.maven.plugins
protobuf-maven-plugin
0.6.1
com.google.protobuf:protoc:3.12.0:exe:${os.detected.classifier}
grpc-java
io.grpc:protoc-gen-grpc-java:1.34.1:exe:${os.detected.classifier}
${project.basedir}/src/main/java
false
compile
compile-custom
```
3、生成GRPC代码,使用ideal,直接在maven操作区,点击compile,编译 api 项目,并同时执行 protobuf-maven-plugin
插件进行生成,如果出现错误,可能需要安装protobuf插件,点击compile之后,会在相应的目录生成代码,目录的设置是根据步骤2,以及proto的设置决定。

等价于 使用mvn命令 protobuf:compile 和protobuf:compile-custom命令编译生成java文件

生成数据类、grpc 服务类 ImplicBases 和 Stub。 请注意,其他插件,如 reactive-grpc 可能会生成其他额外 / 替代类。 然而,它们也可以同样的方式使用。
- ImplicBase类包含基本逻辑,映射虚拟实现到grpc 服务方法。 在 实现服务逻辑 章节中有更多关于这个问题的信息。
- Stub类是完整的客户端实现。 更多信息可以参考 客户端指引 页面。
4、生成的代码,会缺少依赖的jar,逐个加入,大致如下:
```xml
io.grpc
grpc-api
1.31.1
compile
io.grpc
grpc-stub
1.31.1
compile
com.google.protobuf
protobuf-java
3.12.0
compile
io.grpc
grpc-protobuf
1.31.1
compile
```
> Node.js库从运行时加载的 .proto 文件动态生成服务描述和客户端存根的定义,所以使用此语言时没必要生成任何特殊代码。而是在例子客户端和服务端里,我们 require gRPC 库,然后用它的 load() 方法:
> ```javascript
> var grpc = require('grpc');
> var hello_proto = grpc.load(PROTO_PATH).helloworld;
> ```
### 定义服务端
服务端引用 上面生成的jar ,并定义对应 api 方法实现,如下

在这里我们创建了合理的 gRPC 服务器,将我们实现的 Greeter 服务绑定到一个端口。然后我们启动服务器:服务器现在已准备好从 Greeter 服务客户端接收请求。我们将在具体语言对应的文档里更深入地了解这所有的工作是怎样进行的。
### 定义客户端
需要配置提供服务的server的相关信息信息,特别是 address 为必需,
在这个例子里,我们创建了一个阻塞的存根。这意味着 RPC 调用要等待服务器应答,将会返回一个应答或抛出一个异常。 gRPC Java 还可以有其他种类的存根,可以向服务器发出非阻塞的调用,这种情况下应答是异步返回的。
我们创建并填充一个 Request 发送给服务。 我们用请求调用存根的 方法,如果 RPC 成功,会得到一个填充的 回复 ,从其中我们可以获得 返回值。
> 同步 RPC 调用一直会阻塞直到从服务端获得一个应答,这与 RPC 希望的抽象最为接近。另一方面网络内部是异步的,并且在许多场景下能够在不阻塞当前线程的情况下启动 RPC 是非常有用的。
> 在多数语言里,gRPC 编程接口同时支持同步和异步的特点。
### 开箱即用 Starter 组件
gRPC 社区暂时没有提供 Spring Boot Starter 库,以简化我们对 gRPC 的配置。不过国内有大神已经开源了一个。
地址:https://yidongnan.github.io/grpc-spring-boot-starter/zh-CN/client/getting-started.html
特性:
- 在 spring boot 应用中,通过 @GrpcService 自动配置并运行一个嵌入式的 gRPC 服务
- 使用 @GrpcClient 自动创建和管理你的 gRPC Channels 和 stubs
- 支持 Spring Cloud (向 Consul 或 Eureka 或 Nacos 注册服务并获取gRPC服务信息)
- 支持 Spring Sleuth 进行链路跟踪(需要单独引入 brave-instrumentation-grpc)
- 支持对 server、client 分别设置全局拦截器或单个的拦截器
- 支持 Spring-Security 支持metric (基于 micrometer / actuator )也适用于 (non-shaded) grpc-netty
#### 解释客户端组件
以下列表包含您可能在客户端使用到的的所有功能。 如果您不想使用任何高级功能,那么前两个元素可能都是您需要使用的。
- @GrpcClient: 在需要注入的客户端的字段或者 setter 方法上这个注解。 支持 Channel和各种类型的 Stub。 请不要将 @GrpcClient 与 @Autowireed 或 @Inject 一起使用。
目前,它不支持构造函数和 @Bean 方法参数。 这种情况请查看后面 @GrpcClientBean 的使用文档。 注意:
同一应用程序提供的服务只能在 ` ApplicationStartedEvent 之后访问/调用。 连接到外部服务的 Stubs 可以提前使用;从 @PostConstruct / 初始化Bean#afterPropertiesSet()`
开始。
- @GrpcClientBean: 注解会把使用 @GrpcClient 注解的类型注册到 Spring Context 中,方便 @Autowired 和 @Qualifier 的使用。 这个注解可以重复添加到您的
@Configuration 类中。
- Channel: Channel 是单个地址的连接池。 目标服务器可能是多个 gRPC 服务。 该地址将使用 NameResolver 做解析,最终它可能会指向一批固定数量或动态数量的服务端。
- ManagedChannel: ManagedChannel 是 Channel 的一种特殊变体,因为它允许对连接池进行管理操作,例如将其关闭。
- NameResolver、 NameResolver.Factory: 一个用于将地址解析为SocketAddress 列表的类 ,当与先前列出的服务端连表连接失败或通道空闲时,地址将会重新做解析。 参阅 Configuration
-> Choosing the Target。
- ClientInterceptor: 在每个 Channel 处理之前拦截它们。 可以用于日志、监测、元数据处理和请求/响应的重写。 grpc-spring-boot-starter 将自动接收所有带有
@GrpcGlobalClientInterceptor 注解以及手动注册在GlobalClientInterceptorRegistry 上的客户拦截器。 参阅 Configuration -> ClientInterceptor。
- CallCredentials: 管理身份验证的组件。 它可以用于存储凭据和会话令牌。 它还可以用来身份验证,并且使用返回的令牌(例如 OAuth) 来授权实际请求。 除此之外,如果令牌过期并且重新发送请求,它可以续签令牌。
如果您的应用程序上下文中只存在一个 CallCredentials bean,那么 spring 将会自动将其附加到Stub( 非 Channel )。 CallCredentialsHelper工具类可以帮助您创建常用的
CallCredentials 类型和相关的StubTransformer。
- StubFactory: 一个用于从 Channel 创建特定 Stub 的工厂。 可以注册多个 StubFactory,以支持不同类型的 stub。 参阅 Configuration -> StubFactory。
- StubTransformer: 所有客户端的 Stub 的注入之前应用的转换器。 参阅 Configuration -> StubTransformer。
### 增加python的 交互
1.安装python需要的库
```shell
pip install grpcio
pip install grpcio-tools
pip install protobuf
```
2、定义的接口文件还是用上面的 在文件同级目录下,执行如下命令
```shell
python -m grpc_tools.protoc -I ./ --python_out=./ --grpc_python_out=. ./userInfo.proto
```
利用编译工具把proto文件转化成py文件,直接在当前文件目录下运行上述代码即可。
- -I 指定proto所在目录
- -m 指定通过protoc生成py文件
- --python_out指定生成py文件的输出路径
- userInfo.proto 输入的proto文件 执行上述命令后,生成userInfo_pb2.py 和userInfo_pb2_grpc.py这两个文件,将生成的文件拷贝到项目目录下。
3、编写服务端和客户端
### 增加 c++ 交互
#### protobuf编译器下载
##### 安装
Protocol Buffers(protobuf)是一个强大的序列化工具,它需要一个编译器来将其接口定义语言转换为特定的开发语言。
###### Windows版本的安装

- 解压缩
- 首先,从官方发布页面下载适用于Windows的protobuf编译器。
- 将下载的文件解压到一个特定的目录下。
- 配置环境变量
- 在系统的环境变量中,找到`PATH`变量。
- 将protobuf编译器的bin目录添加到PATH变量中。
- 保存更改并关闭环境变量窗口。
###### Mac版本的安装
`使用Homebrew安装`
如果你还没有安装Homebrew,首先需要安装它。然后,使用以下命令安装protobuf:
```shell
brew install protobuf
```
###### Linux版本的安装
[注:原始资料中没有提供Linux的安装方法,因此以下是一个通常的安装方法。]
使用包管理器安装
- 对于Debian和Ubuntu系统:
```shell
sudo apt-get update
sudo apt-get install protobuf-compiler
```
- 对于Red Hat和CentOS:
```shell
sudo yum install protobuf-compiler
```
##### 验证安装
在终端中输入以下命令:
```shell
protoc --version
```
如果显示protobuf的版本信息,则表示安装成功。
##### 生成api接口文件
文件内容
```protobuf
syntax = "proto3";
package tutorial;
option optimize_for = LITE_RUNTIME;
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;
}
```
切换到 proto 文件的定义处,执行如下命令:
```shell
# protoc -I=$SRC_DIR $SRC_DIR/xxx.proto --cpp_out=$DST_DIR
protoc --cpp_out=. ./addressbook.proto
```
其中:
- $SRC_DIR 表示 .proto文件所在的源目录;
- $DST_DIR 表示生成目标语言代码的目标目录;
- xxx.proto 表示要对哪个.proto文件进行解析;
- --cpp_out 表示生成C++代码。
编译完成后,将会在目标目录中生成 xxx.pb.h 和 pb.cc, 文件,将其引入到我们的C++工程中即可实现使用protobuf进行序列化
在C++源文件中包含 xxx.pb.h 头文件,在g++编译时链接 xxx.pb.cc 源文件即可:
```shell
g++ main_test.cpp pb.cc, -o main_test -lprotobuf
```
#### protobuf-cpp 依赖准备
##### 安装Cmake

##### 下载 protobuf-cpp源码

##### 编译代码,生成插件
1、首先进入安装目录,找到cmake目录,进入命令行,执行命令`cmake .`
2、找到cmake目录下的protobuf.sln文件,用VS打开,并执行。

3、执行后,会在该目录生成很多新的文件夹和文件,包括非常重要的Debug文件夹
#### 项目准备
##### 构建项目
这里我们构建一个名为 cpp_server 的项目及解决方案

> 项目创建问题
> 如果是Windows程序,那么WinMain是入口函数,在VS2013中新建项目为“win32项目”,**在VS2019中用“windows桌面向导”创建**
> 如果是控制台程序,那么main是入口函数,在VS2013中新建项目为“win32控制台应用程序”,**在VS2019中新建项目为“控制台应用”**
> 若入口函数指定不当会报错
引入对应资源

主文件 add_person.cpp 内容如下
```c++
#include
#include
#include
#include "pbs/addressbook.pb.h" //注意自己的相对路径
using namespace std;
void serialize_process() {
cout << "serialize_process" << endl;
tutorial::Person person;
person.set_name("Obama");
person.set_id(1234);
person.set_email("1234@qq.com");
tutorial::Person::PhoneNumber* phone1 = person.add_phones();
phone1->set_number("110");
phone1->set_type(tutorial::Person::MOBILE);
tutorial::Person::PhoneNumber* phone2 = person.add_phones();
phone2->set_number("119");
phone2->set_type(tutorial::Person::HOME);
fstream output("person_file", ios::out | ios::trunc | ios::binary);
if (!person.SerializeToOstream(&output)) {
cout << "Fail to SerializeToOstream." << endl;
}
cout << "person.ByteSizeLong() : " << person.ByteSizeLong() << endl;
}
void parse_process() {
cout << "parse_process" << endl;
tutorial::Person result;
fstream input("person_file", ios::in | ios::binary);
if (!result.ParseFromIstream(&input)) {
cout << "Fail to ParseFromIstream." << endl;
}
cout << result.name() << endl;
cout << result.id() << endl;
cout << result.email() << endl;
for (int i = 0; i < result.phones_size(); ++i) {
const tutorial::Person::PhoneNumber& person_phone = result.phones(i);
switch (person_phone.type()) {
case tutorial::Person::MOBILE:
cout << "MOBILE phone : ";
break;
case tutorial::Person::HOME:
cout << "HOME phone : ";
break;
case tutorial::Person::WORK:
cout << "WORK phone : ";
break;
default:
cout << "phone type err." << endl;
}
cout << person_phone.number() << endl;
}
}
int main(int argc, char* argv[]) {
serialize_process();
parse_process();
google::protobuf::ShutdownProtobufLibrary(); //删除所有已分配的内存(Protobuf使用的堆内存)
system("pause");
//return 0;
}
```
> 大致逻辑,通过 person_file 文件进行数据的交互
##### 项目运行
在VS项目中需要使用protobuf时,需要注意的一些配置
1、把src目录的路径加入在附加包含目录
该目录中包含很多使用protobuf需要用到的一些头文件

2、添加所有链接文件(这里有5个,绝对路径 以 lib 结尾的文件)

3、代码生成改为多线程调试

> 至于 Mac电脑 是否可以 在 项目下 直接 通过 CMakeLists.txt 文件指定上面的 环境和 链接 ,未验证
### 拓展
#### 集成到 dubbo
dubbo目前仅java提供grpc的协议支持,其他优待扩展 参考地址: https://developer.aliyun.com/article/728009