连接池化
按说,grpc使用http2,而http2是支持多路复用的,这样说来或许不需要额外的连接池。
真的是这样吗?
通过压测,我发现单个连接的client可以支持最大到9w的qps,并发量再大,cpu占用率会上去,但吞吐量会下来。
为什么会这样的?
猜测大概是由于tcp头阻塞和client锁竞争引起的。
应用层的头阻塞:在http1.1时各个request和response需要排队进行,在没有收到response报文之前,该tcp不能被复用。
虽然http2避免了应用层的头阻塞,但依然存在tcp层的头阻塞。
tcp头阻塞:tcp在并发发包时,接收方可能会先收到后面的包,那么这个包就不能放到socket buf上,只能先放到内核协议栈上,这就是tcp头阻塞。
那么怎么解决呢?
一个成熟的方案是,池化。
通过使用自建连接池,经测qps可以达到40w左右。
总结,如果服务的并发量不高,使用单个client既可以避免大量的tcp TIME_WAIT问题,也可以支撑数万的qps。
当并发量远远超过这个数量级时,可以考虑使用池化方案进行优化。
滴滴在使用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】