1.简介
1.1 gRPC的起源
RPC是Remote Procedure Call的简称,中文叫远程过程调用。用于解决分布式系统中服务之间的调用问题。通俗地讲,就是开发者能够像调用本地方法一样调用远程的服务。所以,RPC的作用主要体现在这两个方面:
-
屏蔽远程调用跟本地调用的区别,让我们感觉就是调用项目内的方法;
-
隐藏底层网络通信的复杂性,让我们更专注于业务逻辑的开发。
长期以来,谷歌有一个名为 Stubby 的通用 RPC 框架,用来连接成千上万的微服务,这些微服务跨多个数据中心并且使用完全不同的技术来构建。
Stubby 的核心 RPC 层每秒能处理数百亿次的互联网请求。Stubby有许多很棒的特性,但无法标准化为业界通用的框架,这是因为它与谷歌内部的基础设施耦合得过于紧密。
2015 年,谷歌发布了开源 RPC 框架 gRPC,这个 RPC 基础设施具有标准化、可通用和跨平台的特点,旨在提供类似 Stubby 的可扩展性、性能和功能,但它主要面向社区。
在此之后,gRPC 的受欢迎程度陡增,很多大型公司大规模采用了gRPC,如 Netflix、Square、Lyft、Docker、CoreOS 和思科。接着,gRPC 加入了云原生计算基金会(Cloud Native Computing Foundation,CNCF),这是最受欢迎的开源软件基金会之一,它致力于让云原生计算具备通用性和可持续性。gRPC 从 CNCF 生态系统项目中获得了巨大的发展动力。
1.2 gRPC的定义
gRPC官网地址:https://grpc.io
gRPC 是由Google开发的一个语言中立、高性能、通用的开源RPC框架,基于ProtoBuf(Protocol Buffers) 序列化协议开发,且支持众多开发语言。面向服务端和移动端,基于 HTTP/2 设计。
在每个 gRPC 发布版本中,字母 g 的含义都不同。比如 1.1 版本的 g 代表 good(优秀),1.2版本的 g 代表 green(绿色)。具体可以参考https://github.com/grpc/grpc/blob/master/doc/g_stands_for.md
gRPC框架是围绕定义服务的思想,显式定义了可以被远程调用的方法,包括入参和出参的信息等。gRPC服务端负责这些方法的具体实现,而客户端拥有这些方法的一个存根(stub),这样就可以远程调用到服务端的方法。
2.Quick Start
接下来,我们通过一个小例子,来感受一下gRPC的开发过程。
2.1 开发步骤
在开发 gRPC 应用程序时,先要定义服务接口,其中应包含如下信息:消费者消费服务的方式、消费者能够远程调用的方法以及调用这些方法所使用的参数和消息格式等。在服务定义中所使用的语言叫作接口定义语言(interface definition language,IDL)。gRPC 使用 Protocol Buffer 作为 IDL 来定义服务接口。
借助服务定义,可以生成服务器端代码,也就是服务器端骨架(skeleton) ,它通过提供低层级的通信抽象简化了服务器端的逻辑。同时,还可以生成客户端代码,也就是客户端存根(stub),它使用抽象简化了客户端的通信,为不同的编程语言隐藏了低层级的通信。就像调用本地函数那样,客户端能够远程调用我们在服务接口定义中所指定的方法。底层的 gRPC 框架处理所有的复杂工作,通常包括确保严格的服务契约、数据序列化、网络通信、认证、访问控制、可观察性等。
2.2 定义Protocol Buffer
Protocol Buffer(简称Protobuf) 是语言中立、平台无关、实现结构化数据序列化的可扩展机制。它就像JSON, 但比JSON体积更小,传输更快,具体可查阅其官网:https://developers.google.cn/protocol-buffers/docs/overview
Protobuf在 gRPC 的框架中主要有三个作用:定义数据结构、定义服务接口,通过序列化和反序列化方式提升传输效率。
Protobuf文件的后缀是.proto,定义以下服务:
syntax = "proto3"; // 表示使用的protobuf版本是proto3。还有一个版本是proto2,建议使用最新版本。
import "google/protobuf/wrappers.proto";// 引入包装类型,没有默认值。下面会讲
option java_multiple_files = true; // 如果是false,则只生成一个java文件。反之生成多个。
option java_package = "com.khlin.grpc.proto"; // 类的包名
option java_outer_classname = "UserProto"; // 想要生成的类的名字
option objc_class_prefix = "khlin"; // 设置Objective-C类前缀,该前缀位于此.proto中所有Objective-C生成的类和枚举之前。似乎Java没用上。
package user; // protobuf消息类型的包类,同样是为了防止命名冲突。
// 定义一个服务
service UserService{
// 简单模式
rpc getUserInfo(UserRequest) returns (UserResponse);
// 客户端流
rpc batchGetUserInfo(stream UserRequest) returns (google.protobuf.StringValue);
// 服务端流
rpc getUserInfoStream(UserRequest) returns (stream UserResponse);
// 双向流
rpc biGetUserInfo(stream UserRequest) returns (stream UserResponse);
}
// 定义一个入参类型
message UserRequest{
string id = 1;
}
// 定义一个出参类型
message UserResponse{
string id = 1;
int32 phoneNumber = 2; // 电话号码
string email = 3; // 邮箱地址
int32 serialNumber = 4; // 序列号
}
下面简单介绍一下数据类型相关知识。
1.序号
每一个字段被赋予了一个唯一的序号,从1开始。Protobuf是通过二进制数据的方式传输,所以需要知道每个位置存储的是什么字段,并且建议一旦定义好就不要修改,防止引起兼容性问题。
2.字段约束
每一个字段可以是以下一种约束:
singular
proto3中的默认约束,最广泛的约束
repeated
类比集合类型
map
类比Map类型
已经舍弃的约束:
optional
proto3中舍弃,在proto2当中表示该字段可为空
required
proto3中舍弃,在proto2当中表示该字段不能为空
3.数据类型
.proto Type | Notes | Java/Kotlin Type |
---|---|---|
double | double | |
float | float | |
int32 | 使用变长编 |
首页 上一页 1 2 3 4 5 6 下一页 尾页 1/6/6 | |
【大 中 小】【打印】 【繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部】 | |
上一篇:Java的Lambda表达式到底是啥? | 下一篇:用Java写一个分布式缓存——缓存.. |