关于grpc和http2的关系的信息
grpc进阶-HTTP/2
title: grpc进阶-HTTP/2
date: 2020-08-22 14:39:55
面试的时候,面试官问我 gRPC 所使用的 HTTP/2 协议有何优势,我支支吾吾没答好。
面试结束后,深刻意识到自身的问题:学习新技术框架,常常不求甚解,缺乏刨根问底的精神。
因此,抽空好好学习一下 HTTP/2 。
也借此勉励各位,千万不能只会 Hello world 。
众所周知,HTTP/1.0 存在如下问题:
当同时有多个HTTP请求时,会造成TCP连接频繁地创建和销毁(三次握手、四次挥手),十分影响性能。
为了解决这个问题,HTTP/1.1 提出了长连接 Keep-Alive :多个HTTP请求可复用一个TCP连接,当没有HTTP请求时,TCP连接还会保持一段时间才关闭。
但随着网站的请求越来越多,对网络通信性能要求越来越高,HTTP/1.1 也逐渐暴露出了一些问题:
为了解决这一系列问题,HTTP/2 出现了。
2009年,谷歌公布了一个实验性协议—— SPDY ,用于解决 HTTP/1.1 的性能问题。
2012年,察觉到 SPDY 协议的趋势,HTTP工作组开始征集 HTTP/2 的建议,并基于 SPDY 制定了第一个 HTTP/2 草案。
2015年,IESG 批准 HTTP/2 和 HPACK 草案,RFC 7540 (HTTP/2) 和 RFC 7541 (HPACK) 发布。
官方文档: RFC 7540
官方介绍:
其中,提到了三个重点:
以下,将从这三点依次介绍。
在 HTTP/1.1 中,头部数据以文本形式传输(ASCII编码:一个字符占用一个字节)。
而在 HTTP/2 中,则采用 HPACK 算法 进行压缩。
HPACK 算法原理大致如下(参考博客: HTTP2 详解 ):
二进制分帧层是 HTTP/2 性能增强的核心 ,存在于应用层 HTTP/2 与传输层 TCP 之间。
它将一个 HTTP/2 请求报文( 流 )划分成更小的 帧(frame) ,再将多个请求的帧合并成一个新的报文交给TCP。
接收方TCP将报文交给二进制分帧层后,它会将每一帧抽离出来,拼接到对应的 HTTP/2 报文中。
多个 HTTP/2 请求复用一个 TCP 报文,从而实现了并发。相比于 HTTP/1.1 中一个请求独占一个 TCP 报文,这就像数据交换中 报文交换到分组交换的改进 。
同时,这也与 进程并发 的思想相同。
帧的结构如下:
主要字段的含义如下:
更详细的字段解析,可查看博客和官方文档。
这也就是上文所提到的:发送方将 HTTP/2 报文(流)分解成不同的帧,并将属于不同 HTTP/2 报文(流)的帧合并在一个 TCP 报文中,然后发送,最后接收方再把它们重新组装起来。
正是由于 HTTP/2 的多路复用, 对于同一个域名,浏览器只需创建一个 TCP 连接 。
由于可以同时发送多个 HTTP/2 报文的帧,那么,优先发送哪些报文的帧,就成了一个问题。
在 HTTP/2 中,每个 HTTP/2 报文(流)都可以被分配优先级,优先级高的先发送。
由于接收方需要缓存每个报文已接受的帧(所有帧到达后,再拼接起来),所以,为了防止发送方发送过快过多,导致接收方缓存溢出,HTTP/2 提供了流量控制。
这与 TCP 流量控制 大同小异。
HTTP/2 新增的第三个强大新功能就是: 服务器可以对一个客户端请求发送多个响应 。换句话说,除了对最初请求的响应外,服务器还可以向客户端推送额外资源,而无需客户端明确地请求。
这是不是类似于 Websocket ?
测试网站:
GRPC的理解
grpc每个流只有一个grpc的数据帧,这个数据帧在传输的时候,会拆成多个http2的数据帧进行传输,然后在接受端,把所有http2的数据帧拼接成grpc的数据帧,再反序列化成请求的结构体。如果一次传输数据过大,在序列化和反序列化的时候,都会占用大量的cpu,不仅仅序列化,单纯的内存复制,也会占用大量cpu,而且,传输的时候,对带宽的影响也是很大的。因此,grpc不推荐一次传输大量数据,如果有大量数据要传输,则使用stream模式。【当然,grpc单次数据传输的大小限制是可以修改的,但是不建议你这么做】【默认最大消息大小为4MB
】
【不断修改增加服务端和客户端消息大小,每次请求不一定需要全部数据,会导致性能上和资源上的浪费】
【grpc协议层是基于http2设计的(但之前看一片测评文章,结构发现文件传输的时候速度有点慢,因为大量数据传输的场景瓶颈在于tcp,如果还在一个tcp上进行多路复用,那只会加剧锁竞争)】
【4 MB 的限制是为了保护没有考虑过消息大小限制的客户端/服务器。 gRPC 本身可以提高很多(100 MB),但大多数应用程序可能会受到轻微攻击或意外内存不足,从而允许该大小的消息。】
【grpc本质是为了提高单连接的利用率,如果单个stream上传输大量的数据,那么其他stream的数据就很难得到及时的传输,grpc适用于大量的请求,但是每次请求的传输数据量不大的情况】
【如果单次传输的数据量过大,建议从新开一个tcp连接,也就是用http1.1,因为在数据量很大的情况下,瓶颈在于底层的tcp】
【同理,在IM系统中,拉消息也建议使用http1.1的接口,避免占用大量的长连带宽,影响下行推送及时性】
【http1.1有维护连接池,每次请求都会独占一个tcp连接,所以,在传输大量数据的场景下,也不会影响到其他请求的数据传输,瓶颈在于机器性能】
grpc 连接池
grpc-源码-网络模型
golang 的grpc库是
grpc server端和服务端网络协议是在tcp基础上的 http2协议,http2协议负责grpc基础的数据传输、连接管理、流控等, 具体的业务层service 定义是基于 protobuf的
整个的网络过程和关键点如下图
说明:
TCP KeepAlive则是为了探测/保鲜(心跳检测,连接错误检测):用于探测对端的状态及网络情况(有可能客户端崩溃、强制关闭了应用、主机不可达等等),也有保鲜功能。比如如防止nat超时。TCP keepalive则是通过发送发送侦测包实现。在Linux中通过net.ipv4.tcp_keepalive_intvl,net.ipv4.tcp_keepalive_probes,net.ipv4.tcp_keepalive_time配置。比如gnet 网络框架中的实现