透过实例demo带你认识gRPC
摘要:gRPC是基于定义一个服务,指定一个可以远程调用的带有参数和返回类型的的方法。在服务端,服务实现这个接口并且运行gRPC服务处理客户端调用。
本文分享自华为云社区《gRPC介绍以及spring demo构架展示》,作者:gentle_zhou。
gRPC,即google Remote Procedure Call Protocol;在gRPC里,客户端可以直接调用不同机器上的服务应用的方法,就像本地对象一样,所以创建分布式应用和服务就变简单了。
gRPC是基于定义一个服务,指定一个可以远程调用的带有参数和返回类型的的方法。在服务端,服务实现这个接口并且运行gRPC服务处理客户端调用。在客户端,有一个stub提供和服务端相同的方法。

数据编码
数据编码即将请求的内存对象转化成可以传输的字节流发送给服务端,并将收到的字节流在转化成内存对象。常见的数据编码方法有JSON,而gRPC则默认选用protobuf。
为什么选用protobuf呢?一个是因为它是谷歌自己的产品,二是它作为一种序列化资料结构的协定,在某些场景下传输的效率比JSON高。
一个.proto文件里的消息格式如下:

而一个典型的JSON格式如下所示:

我们可以看到在JSON里,内存方面,int字段的12345会占据5个字节,bool字段的true会占据4个字节,占据内存就会比较大,编码低效;还有一个缺点就是在JSON里,同一个接口同一个对象,只是int字段的值不同,每次却都还要传输int这个字段名。这样做的好处就是JSON的可读性很高,但同样在编码效率方面就会有所牺牲。
而Protobuf则是选用了VarInts对数字进行编码(VarInts则是动态的,征用了每个字节的最高位MSB,如果是1表示还有后序字节,如果是0表示后面就没字节了,以此来确定表示长度所需要的字节数量,解决了效率问题),同时给每个字段指定一个整数编号,传输的时候只传字段编号(解决了效率和冗余问题)。
但是只传字段编号的话,接收方如何知道各个编号对应哪个字段呢?那就需要靠提前约定了。Protobuf使用.proto文件当做密码本,记录字段和编号的对应关系。

Protobuf 提供了一系列工具,为 proto 描述的 message 生成各种语言的代码。传输效率上去了,工具链也更加复杂了。
请求映射
IDL,Interactive Data Language的缩写,交互式数据语言。
因为我们有.proto文件作为IDL,Protobuf就可以做到RPC描述。比如在.proto文件里定义一个Greeter服务,其中有一个 SayHello 的方法,接受 HelloRequest 消息并返回 HelloReply 消息。如何实现这个 Greeter 则是语言无关的,所以叫 IDL。gRPC 就是用了 Protobuf 的 service 来描述 RPC 接口的。

gRPC 在底层使用的是 HTTP/2 协议。这个 HTTP 请求用的是 POST 方法,对应的资源路径则是根据 .proto 定义确定的。我们前面提到的 Greeter 服务对应的路径是/demo.hello.Greeter/SayHello 。
一个 gRPC 定义包含三个部分,包名、服务名和接口名,连接规则如下
/${包名}. ${服务名}/ ${接口名}
SayHello的包名是demo.hello,服务名是Greeter,接口名是SayHello,所以对应的路径就是 /demo.hello.Greeter/SayHello。
gRPC 支持三种流式接口,定义的办法就是在参数前加上 stream 关键字,分别是:请求流、响应流和双向流。
- 第一种叫请求流,可以在 RPC 发起之后不断发送新的请求消息。此类接口最典型的使用场景是发推送或者短信。
- 第二种叫响应流,可以在 RPC 发起之后不断接收新的响应消息。此类接口最典型的使用场景是订阅消息通知。
- 最后一种是双向流。可以在 RPC 发起之后同时收发消息。此类接口最典型的使用场景是实时语音转字幕。
如下就是普通接口和三种流式接口的结构样式:

最简单的gRPC(非流式调用,unary)请求内容和相应内容如下所示:


如果单看非流式调用,也就是 unary call,gRPC 并不复杂,跟普通的 HTTP 请求也没有太大区别。我们甚至可以使用 HTTP/1.1 来承载 gRPC 流量。但是gRPC 支持流式接口,这就有点难办了。
我们知道,HTTP/1.1 也是支持复用 TCP 连接的。但这种复用有一个明显的缺陷,所有请求必须排队。也就是说一定要按照请求、等待、响应、请求、等待、响应这样的顺序进行。先到先服务。而在实际的业务场景中肯定会有一些请求响应时间很长,客户端在收到响应之前会一直霸占着TCP连接。在这段时间里别的请求要么等待,要么发起新的 TCP 连接。在效率上确实有优化的余地。一言以蔽之,HTTP/1.1 不能充分地复用 TCP 连接。
后来,HTTP/2 横空出世!通过引入 stream 的概念,解决了 TCP 连接复用的问题。你可以把 HTTP/2 的 stream 简单理解为逻辑上的 TCP 连接,可以在一条 TCP 连接上并行收发 HTTP 消息,而无需像 HTTP/1.1 那样等待。所以 gRPC 为了实现流式接品,选择使用 HTTP/2 进行通信。所以,前文的 Greeter 调用的实际通信内容长这个样子。


HTTP/2 的 header 和 data 使用独立的 frame(帧,简单来说也是一种 Length-Prefixed 消息,是 HTTP/2 通信的基本单位) 发送,可以多次发送。
springboot里的grpc demo
整个项目可以分成三个project:
- grpc-springboot-demo-api:proto文件(syntax=“proto3”; 定义服务,定义请求体,定义回应内容)写好之后用maven-install来编译生成所需的类;
- grpc-springboot-demo-server:pom文件(springboot的启动依赖,grpc的依赖, api项目的依赖),springboot的启动类,GRPC服务器的启动类,提供服务的业务逻辑实现类
- grpc-springboot-demo-consumer:pom文件(springboot的启动依赖,grpc的依赖, api项目的依赖),springboot启动类(与服务端启动类无差异),gRPC 客户端(主要作用是监听 gRPC 服务端,开启通道)。
对应MVC关系就是:
grpc-springboot-demo-api就是service(接口,为提供实现);
grpc-springboot-demo-server就相当于serviceImpl(service的实现类);
grpc-springboot-demo-consumer就是controller的角色。
具体代码可以看:https://blog.csdn.net/Applying/article/details/115024675
拓展
repeated限定修饰符
repeated代表可重复,我们可以理解为数组。比如下面的代码:
syntax = "proto3";//指定版本信息,不指定会报错 message Person //message为关键字,作用为定义一种消息类型
{
string name = 1; //姓名
int32 id = 2; //id
string email = 3; //邮件
} message AddressBook
{
repeated Person people = 1;
}
编译器就会把Person认定为数组,而我们在使用Person,用add往里面添加信息,代码如下:
AddressBook addBopookReq = AddressBook.newBuilder().addName("Lily").build();
就不需要指定index了,直接往数组里添加了一个新的addressbook,它的名字属性则是Lily。
参考资料
- https://developers.google.com/protocol-buffers/docs/overview
- https://taoshu.in/grpc.html
- https://grpc.io/
- https://grpc.io/docs/languages/java/quickstart/
- https://blog.csdn.net/tennysonsky/article/details/73921025
透过实例demo带你认识gRPC的更多相关文章
- Vue UI组件 开发框架 服务端 辅助工具 应用实例 Demo示例
Vue UI组件 开发框架 服务端 辅助工具 应用实例 Demo示例 element ★11612 - 饿了么出品的Vue2的web UI工具套件 Vux ★7503 - 基于Vue和WeUI的组件库 ...
- ArcGIS API for JavaScript开发环境搭建及第一个实例demo
原文:ArcGIS API for JavaScript开发环境搭建及第一个实例demo ESRI公司截止到目前已经发布了最新的ArcGIS Server for JavaScript API v3. ...
- Android ListFragment实例Demo(自己定义适配器)
上一篇文章介绍了ListFragment,当中的ListView并没有自己定义适配器,实际上在实际开发中常会用到自己定义适配器,是实现更复杂的列表数据展示. 所以这篇文章添加了自己定义适配器.来进行L ...
- 3 weekend110的hadoop中的RPC框架实现机制 + hadoop中的RPC应用实例demo
hadoop中的RPC框架实现机制 RPC是Remotr Process Call, 进程间的远程过程调用,不是在一个jvm里. 即,Controller拿不到Service的实例对象. hadoop ...
- mybatis 学习笔记 -详解mybatis 及实例demo
快速入门1 要点: 首先明白mybatis 是什么 这是一个持久层的框架.之前叫做ibatis.所以,在它的代码中出现ibatis这个词的时候,不要感到惊讶.不是写错了,它确实就是这个样子的. 首先, ...
- 一个Demo带你彻底掌握View的滑动冲突
本文已授权微信公众号:鸿洋(hongyangAndroid)在微信公众号平台原创首发. 近期在又一次学习Android自己定义View这一块的内容.遇到了平时开发中常常碰到的一个棘手问题:View的滑 ...
- Android之SlideMenu实例Demo
年末加班基本上一周都没什么时候回家写代码,回到家就想睡觉,周末难得有时间写个博客,上次写了一篇关于SlideMenu开源项目的导入问题,这次主要讲讲使用的问题,SlideMenu应用的广泛程度就不用说 ...
- Android微信分享功能实例+demo
Android微信分享功能实例 1 微信开放平台注册 2 获得appId,添加到程序中,并运行程序 3 使用应用签名apk生成签名,添加到微信开放平台应用签名,完成注册 4 测试分享功能. 有问题请留 ...
- Bootstrap历练实例:带徽章的列表组
向列表组添加徽章 我们可以向任意的列表项添加徽章组件,它会自动定位到右边.只需要在 <li> 元素中添加 <span class="badge"> 即可.下 ...
随机推荐
- 4.1 ROS元功能包
4.1 ROS元功能包 场景:完成ROS中一个系统性的功能,可能涉及到多个功能包,比如实现了机器人导航模块,该模块下有地图.定位.路径规划...等不同的子级功能包.那么调用者安装该模块时,需要逐一的安 ...
- 模拟web服务器http请求应答
我们在浏览器打开网页,其实是向远端服务器提出页面发送请求,远端服务器在接到请求后,就开始执行请求页面的程序文件,然后将执行结果通过html格式,发送到你的浏览器,再显示出来.以下用百度(www.bai ...
- Cadence 错误合集
1.原理图DRC出现如下错误"Duplicate Pin Name "GND" found on Packag" 解决方案:原因是元件引脚重复定义,可以进行重新 ...
- Python 与 C++ 向量
Python 与 C++ 向量 Python 和 C++ 对比 我们再回到向量!你已经学习了如何声明一个空的向量. 在下面的代码中,你可以比较 Python 列表和 C++ 向量的语法.你会看到,C+ ...
- 无单位数字和行高 —— 别说你懂CSS相对单位
前段时间试译了Keith J.Grant的CSS好书<CSS in Depth>,其中的第二章<Working with relative units>,书中对relative ...
- 浏览器默认解析文档模式IE7
背景:实习期间开发了一套页面,兼容到IE8+,可是在给产品体验时,他的QQ浏览器默认解析到了文档模式IE7,出现了页面的兼容性问题.问题排查:QQ浏览器目前有两个模式,极速模式和兼容模式,其中极速模式 ...
- html5知识点补充—mark元素的使用
使用mark元素高亮文本 利用mark元素,文档作者可以高亮显示文档中的某些文本以达到醒目的效果. 如果用户在站点进行搜索,搜索页面中的关键字可以高亮显示.这时,就可以很好的利用到mark元素.不选用 ...
- 下载jar包方法
第一种通用下载jar包方法 apache官网下载jar包地址:http://ftp.cuhk.edu.hk/pub/packages/apache.org/ 第二种通用下载jar包方法 mave ...
- js获取异步方法里面的数据
这里介绍 五种方法(说白了本质 就三种) 1.callback回调函数 function getData(callback){ setTimeout(()=>{ let name = '哈哈 ...
- Blazor组件自做三 : 使用JS隔离封装ZXing扫码
Blazor组件自做三 : 使用JS隔离封装ZXing扫码 本文基础步骤参考前两篇文章 Blazor组件自做一 : 使用JS隔离封装viewerjs库 Blazor组件自做二 : 使用JS隔离制作手写 ...