摘要: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:

  1. grpc-springboot-demo-api:proto文件(syntax=“proto3”; 定义服务,定义请求体,定义回应内容)写好之后用maven-install来编译生成所需的类;
  2. grpc-springboot-demo-server:pom文件(springboot的启动依赖,grpc的依赖, api项目的依赖),springboot的启动类,GRPC服务器的启动类,提供服务的业务逻辑实现类
  3. 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。

参考资料

  1. https://developers.google.com/protocol-buffers/docs/overview
  2. https://taoshu.in/grpc.html
  3. https://grpc.io/
  4. https://grpc.io/docs/languages/java/quickstart/
  5. https://blog.csdn.net/tennysonsky/article/details/73921025

点击关注,第一时间了解华为云新鲜技术~

透过实例demo带你认识gRPC的更多相关文章

  1. Vue UI组件 开发框架 服务端 辅助工具 应用实例 Demo示例

    Vue UI组件 开发框架 服务端 辅助工具 应用实例 Demo示例 element ★11612 - 饿了么出品的Vue2的web UI工具套件 Vux ★7503 - 基于Vue和WeUI的组件库 ...

  2. ArcGIS API for JavaScript开发环境搭建及第一个实例demo

    原文:ArcGIS API for JavaScript开发环境搭建及第一个实例demo ESRI公司截止到目前已经发布了最新的ArcGIS Server for JavaScript API v3. ...

  3. Android ListFragment实例Demo(自己定义适配器)

    上一篇文章介绍了ListFragment,当中的ListView并没有自己定义适配器,实际上在实际开发中常会用到自己定义适配器,是实现更复杂的列表数据展示. 所以这篇文章添加了自己定义适配器.来进行L ...

  4. 3 weekend110的hadoop中的RPC框架实现机制 + hadoop中的RPC应用实例demo

    hadoop中的RPC框架实现机制 RPC是Remotr Process Call, 进程间的远程过程调用,不是在一个jvm里. 即,Controller拿不到Service的实例对象. hadoop ...

  5. mybatis 学习笔记 -详解mybatis 及实例demo

    快速入门1 要点: 首先明白mybatis 是什么 这是一个持久层的框架.之前叫做ibatis.所以,在它的代码中出现ibatis这个词的时候,不要感到惊讶.不是写错了,它确实就是这个样子的. 首先, ...

  6. 一个Demo带你彻底掌握View的滑动冲突

    本文已授权微信公众号:鸿洋(hongyangAndroid)在微信公众号平台原创首发. 近期在又一次学习Android自己定义View这一块的内容.遇到了平时开发中常常碰到的一个棘手问题:View的滑 ...

  7. Android之SlideMenu实例Demo

    年末加班基本上一周都没什么时候回家写代码,回到家就想睡觉,周末难得有时间写个博客,上次写了一篇关于SlideMenu开源项目的导入问题,这次主要讲讲使用的问题,SlideMenu应用的广泛程度就不用说 ...

  8. Android微信分享功能实例+demo

    Android微信分享功能实例 1 微信开放平台注册 2 获得appId,添加到程序中,并运行程序 3 使用应用签名apk生成签名,添加到微信开放平台应用签名,完成注册 4 测试分享功能. 有问题请留 ...

  9. Bootstrap历练实例:带徽章的列表组

    向列表组添加徽章 我们可以向任意的列表项添加徽章组件,它会自动定位到右边.只需要在 <li> 元素中添加 <span class="badge"> 即可.下 ...

随机推荐

  1. 时间工具类之"获取相差天数"

    一.时间工具类DateUtils之"获取相差天数" 1 /** 2 * 相差天数 3 * 4 * <p>TODO 方法功能描述 5 * 6 * @param start ...

  2. Streamlit:快速数据可视化界面工具

    目录 Streamlit简介 Streamlit使用指南 常用命令 显示文本 显示数据 显示图表 显示媒体 交互组件 侧边栏 缓存机制 Streamlit使用Hack Streamlit的替代品 相关 ...

  3. FR9833 32V转5V

  4. MySQL索引机制(详细+原理+解析)

    MySQL索引机制 永远年轻,永远热泪盈眶 一.索引的类型与常见的操作 前缀索引 MySQL 前缀索引能有效减小索引文件的大小,提高索引的速度.但是前缀索引也有它的坏处:MySQL 不能在 ORDER ...

  5. 大数据学习之路之ambari配置(二)

    按照网上的教程配置,发现配置到hadoop虚拟机内存就开始不够了,心累

  6. Linux_文件传输工具_FileZilla

    什么是FileZilla? FileZilla是一个免费开源的FTP软件,分为客户端版本和服务器版本,具备所有的FTP软件功能.可控性.有条理的界面和管理多站点的简化方式使得Filezilla客户端版 ...

  7. JAVASE Scanner

    package com.huang.boke.flowPath;import java.util.Scanner;public class test01 { public static void ma ...

  8. 2021年Java后端技术知识体系

    -----2021/1/22

  9. 获取ul中li的value值

    <script> $(function(){ $(".month-list").find("li").click(function(){ var t ...

  10. asp.net core启动源码以及监听,到处理请求响应的过程

    摘要 asp.net core发布至今已经将近6年了,很多人对于这一块还是有些陌生,或者说没接触过:接触过的,对于asp.net core整个启动过程,监听过程,以及请求过程,响应过程也是一知半解,可 ...