gRPC
2023-06-08 08:40:09 17 举报
AI智能生成
gRPC
作者其他创作
大纲/内容
什么是gRPC
gRpc 是一个高性能、开源和通用的 RPC 框架,面向移动和 <b>HTTP/2 设计</b>。目前提供 C、Java 和 Go 语言版本,分别是:grpc, grpc-java, grpc-go. 其中 C 版本支持 C, C++, Node.js, Python, Ruby, Objective-C, PHP 和 C# 支持.
<b>gRPC 基于 HTTP/2 标准设计,带来诸如双向流、流控、头部压缩、单 TCP 连接上的多复用请求等特。这些特性使得其在移动设备上表现更好,更省电和节省空间占用</b>
官方文档
为什么要使用gRPC
<b>生态好(依靠云原生):</b>背靠Google。还有比如nginx也对grpc提供了支持
<b>跨语言:</b>跨语言,且自动生成sdk
<b>性能高:</b>比如protobuf性能高过json, 比如http2.0性能高过http1.1
<b>强类型:</b>编译器就给你解决了很大一部分问题
<b>流式处理(基于http2.0)</b>:支持客户端流式,服务端流式,双向流式
gRPC的传输介质-ProtoBuf
Protobuf是由Google开发的二进制格式,用于在不同服务之间序列化数据。是一种<b>IDL(interface description language)</b>语言
具统计比json快6倍
<b>ProtoBuf的数据格式(为什么ProtoBuf比 json效率高?)</b>
<ul><li><b>体积小-无需分隔符:</b><font color="#e74f4c">TLV存储方式</font>不需要分隔符(逗号,双引号等)就能分隔字段,减少了分隔符的使用</li><li><b>体积小-空字段省略</b>:若字段没有被设置字段值,那么该字段序列化时的数据是完全不存在的,即不需要进行编码,而json会传key和空值的value</li><li><b>体积小-tag二进制表示:</b>是用字段的数字值然后转换成二进制进行表示的,比json的key用字符串表示更加省空间</li><li><b>编解码快:</b>tag的里面存储了字段的类型,可以直接知道value的长度,或者当value是字符串的时候,则用length存储了长度,可以直接从length后取n个字节就是value的值,而如果不知道value的长度,我们就必须要做字符串匹配</li></ul>
<b>gRPC基于 Http2.0(Http2.0优势)</b>
<b>二进制分帧(frame)</b><br>
在二进制分帧层上,<b>HTTP 2.0 会将所有传输的信息分割为更小的消息和帧</b>,并对它们采用<font color="#e74f4c">二进制格式的编码</font> ,其中HTTP1.x的首部信息会被封装到Headers 帧,而我们的request body则封装到Data帧里面。<b>帧是数据传输的最小单位</b>, <font color="#e74f4c">以二进制传输代替原本的明文传输</font>
<b>HTTP 2.0 所有的通信都在一个连接(TCP连接)上完成</b>,这个连接可以承载任意数量的双向数据流。相应地,每个数据流以消息的形式发送,而消息由一或多个帧组成,这些帧可以乱序发送,然后再根据每个帧首部的流标识符重新组装。
<b>HTTP/2 的四个概念</b>
<ul><li><b>Connection</b> :1 个 TCP 连接,包含 1 个或者多个 stream。所有通信都在一 个 TCP 连接上完成,此连接可以承载任意数量的双向数据流。 </li><li><b>Stream:</b>一个双向通信的数据流,包含 1 条或者多条 Message。每个数据流都有一个唯一的标识符和可选的优先级信息,用于承载双向消息。流是连接中的一个虚拟信道,可以承载双向消息传输。每个流有唯一整数标识符。 <font color="#e74f4c">为了防止两端流ID冲突,客户端发起的流具有奇数ID,服务器端发起的流具有偶数ID</font>。 </li><li><b>Message:</b>消息是指逻辑上的HTTP消息(请求/响应)。一系列数据帧组成了一个完整的消息。比如一系列DATA帧和一个HEADERS帧组成了请求消息。 </li><li><b>Frame:</b>最小通信单位,以<font color="#e74f4c">二进制压缩格式</font>存放内容。来自不同数据流的帧可以交错发送,然后再根据每个帧头的数据流标识符重新组装。</li></ul>
<ul><li><b>在一个TCP连接中可以同时存在多个Stream,可以并发执行。但是一个Stream内部的多个Fream不能乱序。</b></li><li><span style="font-size: inherit;">流有顺序,帧没有</span><br></li></ul>
<b>Frame结构</b>
<b>Frame 由 <font color="#e74f4c">Frame Header</font> 和 <font color="#e74f4c">Frame Payload</font> 两部分组成</b>
<b>Length:</b> 帧有效负载的长度(数据的长度,不包含头部),注意:帧头的 9 个八位字节不包含在此长度值中。
<b>Type:</b> 这 8 位用来表示帧类型的。帧类型确定帧的格式和语义。实现方必须忽略并 丢弃任何类型未知的帧。
<b>Flags:</b>标志位,常用的标志位有 END_HEADERS 表示头数据结束,相当于 HTTP/1 里头后的空行(“\r\n”)。
<b>R:</b> 保留的 1 位。该位的语义未定义,发送时必须保持未设置 (0x0),接收时必 须忽略
<b>Stream Identifier:</b> 流标识符,表示为无符号 31 位整数。由<font color="#e74f4c">客户端发起的流必须使用`奇数`编号的流标识符;那些由服务器发起的必须使用`偶数`编号的流标识符</font>。DATA 帧 必须与某一个流相互关联。
<b><font color="#4669ea">stream ID 的作用</font></b>
实现多路复用的关键。接收端的实现可以根据这个 ID 并发、组装消息。 同一个 stream 内 frame 必须是有序的。
推送依赖性请求的关键。<font color="#e74f4c">客户端发起的流是奇数编号,服务端发起的流是 偶数编号</font>
<b>头部压缩(HPACK)</b>
<b>为什么要压缩?</b>
<ul><li>在 HTTP/1 中,HTTP 请求和响应都是由「状态行、请求 / 响应头部、消息主 体」三部分组成。一般而言,消息主体都会经过 gzip 压缩,或者本身传输的就是压缩过后的二进制文件(例如图片、音频),但<b>状态行和头部却没有经过任 何压缩,直接以纯文本传输<br></b></li><li>根据 HTTP Archive 的统计,当前平均每个页面都会产生上百个请求。越来越 多的请求导致消耗在头部的流量越来越多,尤其是每次都要传输 UserAgent、 Cookie 这类不会频繁变动的内容,完全是一种浪费</li></ul>
一个页面的抓包结果。可以看到,传输头部的网络开销 超过 100kb,比 HTML 还多
HTTP/2协议中定义了<b> HPACK</b>,这是一种新的压缩方法,它消除了多余的 header 字段,将漏洞限制到已知的安全攻击,并且在受限的环境中具有有限的内存需求。<b>HPACK 格式特意被设计成简单且不灵活的形式</b>:两种特性都降低了由于实现错误而引起的互操作性或安全性问题的风险;没有定义扩展机制,只能通过定义完整的替换来更改格式
<b>如何进行头部压缩?</b>
维护一份相同的<b>静态表(Static Table)</b>,包含常见的头部名称,以及常见的头部名称与值的组合(静态表内容共61项,索引号1-61);
维护一份相同的<b>动态表(Dynamic Table)</b>,当一个header name 或者 header value在静态表中不存在,会被插入动态表中,可以动态地添加内容 (动态表索引从62开始);<br><ul><li>客户端和服务端会共同维护一份动态表</li><li>第一次发送的时候需要明文发送(要经过Huffman编码),第二次及第N次发送索引号</li></ul>
<b>对不存在的头部使用哈夫曼编码(Huffman Coding),并动态缓存到索引 (动态表)</b>
<b><font color="#314aa4">静态表</font></b>
一个预定义且不可更改的 header 字段列表。先定义好的内容,只有固定的几十个值,如果要发送的值<font color="#e74f4c">符合静态表时,用对应的 Index 替换即可</font>,这样就大 大压缩了头部的大小,如果遇到不在静态表中的值,就会用到动态表
<b><font color="#314aa4">动态索引 & 动态表 </font></b>
<ul><li>动态表是一个由先进先出的队列维护的有空间限制的表,同样维护的是头部与对应的索引。</li><li><font color="#e74f4c"><b>每个动态表只针对一个连接(TCP)</b></font>,每个连接的压缩解压缩的上下文有且仅有一个动态表。</li><li>那么动态表就是,当一个头部没有出现过的时候,会把他插入动态表中, 索引从62开始</li></ul>
<b>多路复用(Multiplexing)</b><br>
http1.1中,浏览器客户端在同一时间,针对同一域名下的请求有一定数量的限制,超过限制数目的请求会被阻塞。这也是为何一些站点会有多个静态资源 CDN 域名的原因之一。
浏览器使用并行连接, 但它们会将向<b>同一个域名请求的并行连接的总数限制为一个较小的值</b>。 浏览器同域名请求的最大并发数限制
<b>http 2.0 连接都是持久化的</b>,而且<b>客户端与服务器之间也只需要一个连接(每个 域名一个连接)</b>即可。http2连接可以承载数十或数百个流的复用,多路复用意 味着来自很多流的数据包能够混合在一起通过同样连接传输。当到达终点时, 再根据不同帧首部的流标识符重新连接将不同的数据流进行组装
<ul><li>单个 HTTP/2 连接可以包含多个并发打开的 stream 流,任一一个端点都可能交叉收到来自多个 stream 流的帧。 </li><li>stream 流可以单方面建立和使用,也可以由客户端或服务器共享。 </li><li>任何一个端都可以关闭 stream 流。 </li><li>在 stream 流上发送帧的顺序非常重要。收件人按照收到的顺序处理帧。特别是,HEADERS 和 DATA 帧的顺序在语义上是重要的。 </li><li>stream 流由整数标识。stream 流标识符是由发起流的端点分配给 stream 流的。</li></ul>
<b>服务器推送(Server Push)</b>
<b>服务器可以对一个客户端请求发送多个响应,服务器向客户端推送资源无需客户端明确地请求</b>。并且,服务端推送能把客户端所需要的资源伴随着 index.html一起发送到客户端,省去了客户端重复请求的步骤
当服务端需要主动推送某个资源时,便会发送一个 Frame Type 为 <b><font color="#e74f4c">PUSH_PROMISE </font></b>的 Frame,里面带了 PUSH 需要新建的 Stream ID。意思是告诉客户端:接下来我要用这个 ID 向你发送东西,客户端准备好接着。客户端解析 Frame 时,发现它是一个 PUSH_PROMISE 类型,便会准备接收服务端要推送的流
<font color="#314aa4"><b>http2.0性能瓶颈</b></font>
启用http2.0后会给性能带来很大的提升,但同时也会带来新的性能瓶颈。因为现在<font color="#e74f4c"><b>所有的压力集中在底层一个TCP连接之上,TCP很可能就是下一个性能瓶颈,</b></font>比如单个TCP packet丢失导致整个连接阻塞,无法逃避,此时所有消息都会受到影响
QUIC协议代替TCP协议中关于可靠、流量控制的部分
0 条评论
下一页