grpc流式传输,grpc 流控
GRPC 浅析
IDL(proto buffer) + RPC
netty:异步/事件驱动的 网络应用程序服务器框架(高性能)
Http2:流式、双向
protobuf:序列化(节省网络带宽)
IDL定义示例:
Starting from a service definition in a .proto file, gRPC provides protocol buffer compiler plugins that generate client- and server-side code.
Http:如果没有API文档就不知道细节,
GRPC:IDL就相当于API文档,可以同时
开发顺序:
Http:先服务端后客户端
GRPC:可以同时
跨语言:都支持多语言
性能:GRPC远差于Thrift,因为用了Http2,各大server目前支持不完善
易用性:GRPC尚未完全提供连接池、服务自动发现、进程内负载均衡等高级特性,需要开发人员额外的封装;
多语言
Http2特性:如stream
易用性:GRPC尚未完全提供连接池、服务自动发现、进程内负载均衡等高级特性,需要开发人员额外的封装
grpc传递文件
2.大文件传输,其他的都一样,唯一不同的是,小文件直接将所有读取到内存中,但是大文件不可能一下都读取进来,这里就需要指定每次传送的字节流大小。需要注意的是,在指定好buf空间大小的时候,最后一次如果不把这个空间填满(比如文件大小为1000,每次读400,那第三次就是200空200),这种情况下接收方会还是接收到完整大小的buf空间,会对内容有影响,所以这里会进行一定量的计算,主要是最后一次传输的大小得和剩余大小相同。
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在C++中的简单使用流程
参考:
????????????
1.gRPC默认使用protocal buffers
2.第一步是在.proto中定义service:service serviceName? {rpc function(){}}
? ? ? ? 有四种类型的方法:
? ? ? ? 1)简单的rpc,就像普通的函数调用一样
? ? ? ? 2)服务端流式rpc:在响应类型前加stream
????????3)客户端流式rpc:在请求类型前加stream
? ? ? ? 4)双向流失rpc:在请求和响应前加stream
3.从 .proto 的服务定义中生成 gRPC 客户端和服务器端的接口:make xx.grpc.pb.cc xx.pb.cc
4.创建服务器:
? ? ? ? 1)实现service定义的生成的服务接口:做服务的实际的“工作”。
????????2)运行一个 gRPC 服务器,监听来自客户端的请求并返回服务的响应。
5.创建客户端:
? ? ? ? 1)创建一个存根
? ? ? ? 2)调用服务的方法
(一) etcd3-server-API
发送到etcd服务器的每个API请求都是gRPC远程过程调用。etcd3中的RPC根据功能分类为:
1、处理etcd key空间的API包括:
2、管理集群本身的API包括:
etcd3中的所有gRPC都遵循相同的格式。每个gRPC都有一个函数Name,该函数NameRequest作为参数并NameResponse作为响应返回。详细gRPC API描述:
来自etcd API的所有响应都有一个附加的响应头,其中包含响应的集群元数据:
Cluster_ID - 生成响应的集群的ID。
Member_ID - 生成响应的成员的ID。
Reversion - 生成响应时key-value存储的修订版。
Raft_Term - 生成响应时成员的Raft术语。
2.1 ? ?? Key-Value
Key-Value 是KV API可以操作的最小单位。每个key-value对都有许多以 protobuf格式 定义的字段:
Key --- 以字节为单位的key,?不允许使用空ke'y。
Value --- 以字节为单位的value。
Version --- 版本是key的版本。删除version将重置为零,并且对key的任何修改都会增加其版本。
Create_Revision --- 最后一次创建key的版本号。
Mod_Revision --- 最后一次修改key的版本号。
Lease --- 附加到key的Lease的ID。如果Lease为0,表明没有将任何Lease附加到key。
2.2 ? ?? Range ? Request
使用Range API调用从KV存储中获取key,其中包含RangeRequest:
Key,Range_End --- 要获取的key范围。
Limit ---限制请求返回的最大key数量。当limit设置为0时,将其视为无限制。
Revision --- 用于key Range内的KV存储的版本范围限制。如果revision小于或等于零,则范围超过最新的key-value存储如果压缩修订,则返回ErrCompacted作为响应。
Sort_Order --- 请求倒序还是顺序。
Sort_Target --- 要排序的类型(key, value, version, create_version, mod_version)。
Serializable --- 设置范围请求以使用可序列化的成员本地读取。默认情况下,Range是可线性化的;?它反映了该集群目前的共识。为了获得更好的性能和可用性,为了换取可能的过时读取,可在本地提供可序列化范围请求,而无需与群集中的其他节点达成共识。
Keys_Only --- 仅返回key而不返回value。
Count_Only --- 仅返回range中key的计数。
Min_Mod_Revision --- key mod version的下限;?
Max_Mod_Revision --- key mod version的上限;?
Min_Create_Revision --- key create version的下限;?
Max_Create_Revision --- key create version的上限;?
2.3 ? ?? Range ? Response :
Header ---见ResponseHeader
Kvs --- 范围请求匹配的key-value对列表。当Count_Only设置为true时,Kvs为空。
More --- 表明RangeRequest中的limit为true。
Count --- 满足范围请求的key总数。
2.4 ? ?? Put ? Request
通过发出Put Request将key-value保存到KV存储中:
Key --- 放入key-value存储区的key的名称。
Value --- 与key-value存储中的key关联的值(以字节为单位)。
Lease ---与key-value存储中的key关联的Lease ID。Lease值为0表示没有Lease。
Prev_Kv --- 设置时,在从此Put请求更新之前使用key-value对数据进行响应。
Ignore_Value --- 设置后,更新key而不更改其当前值。如果key不存在,则返回错误。
Ignore_Lease --- 设置后,更新key而不更改其当前Lease。如果key不存在,则返回错误。
2.5 ? ?? Put ? Response
Header ---见ResponseHeader
Prev_Kv --- 由Putif?Prev_Kv设置的key-value对PutRequest。
2.6 ? ?? Delete Range Request
使用该DeleteRange呼叫删除key范围,其中DeleteRangeRequest:
Key,Range_End - 要删除的key范围。
Prev_Kv - 设置后,返回已删除key-value对的内容。
2.6? ??Delete Range Request
Deleted --- 已删除的key数。
Prev_Kv --- DeleteRange操作删除的所有key-value对的列表。
2.7 ? ?? Transaction
事务是KV存储上的原子If / Then / Else构造。它提供了一个原语,用于将请求分组在原子块(即then / else)中,这些原子块的执行基于key-value存储的内容被保护(即if)。事务可用于保护key免受意外的并发更新,构建比较和交换操作以及开发更高级别的并发控制。
事务可以在单个请求中以原子方式处理多个请求。对于key-value存储的修改,这意味着商店的修订仅针对事务递增一次,并且事务生成的所有事件将具有相同的修订。但是,禁止在单个事务中多次修改同一个key。
所有交易都通过比较结合来保护,类似于“如果”声明。每次比较都会检查商店中的单个key。它可以检查值的缺失或存在,与给定值进行比较,或检查key的修订版本或版本。两种不同的比较可适用于相同或不同的key。所有的比较都是原子地应用的;?如果所有比较都为真,则说事务成功并且etcd应用事务的then /?successrequest块,否则称其失败并应用else /?failurerequest块。
每个比较都编码为一条Compare消息:
Result?--- 逻辑比较操作的类型(例如,等于,小于等)。
Target --- 要比较的key-value字段。key的版本,创建修订版,修改版本或值。
Key --- 比较的key。
Target_Union --- 用于比较的用户指定数据。
在处理比较块之后,事务应用一个请求块。块是RequestOp消息:
Request_Range --- a?RangeRequest。
Request_Put --- a?PutRequest。钥匙必须是唯一的。它可能不与任何其他Puts或Deletes共享key。
Request_Delete_Range --- a?DeleteRangeRequest。它可能不会与任何Puts或Deletes请求共享key。
总之,一个事务是通过TxnAPI调用发出的,它需要TxnRequest:
Compare --- 表示保护事务的术语组合的谓词列表。
Success --- 如果所有比较测试评估为true,则处理请求列表。
Failure --- 如果任何比较测试评估为false,则处理的请求列表。
客户端收到TxnResponse来自Txn呼叫的消息:
Success --- 无论是Compare评估为真还是假。
Responses ---?Success如果成功,则应用块的结果对应的响应列表为true或Failure if成功为false。
该Responses列表对应于应用RequestOp列表的结果,每个响应编码为ResponseOp:
Watch API提供基于事件的接口,用于异步监视key更改。etcd3 watch通过持续watch当前或历史的给定修订,等待key的更改,并将key更新流回客户端。
3.1? ? Events
每个key的每次更改都用Event消息表示。一个Event消息同时提供更新的数据和更新的类型:
Type --- 事件的类型。PUT类型表示新数据已存储到Key中。DELETE表示Key已删除。
KV --- 与事件关联的KeyValue。PUT事件包含当前的kv对。kv.Version = 1的PUT事件表示创建key。DELETE事件包含已删除的key,其修改修订版设置为删除修订版。
Prev_KV --- 紧接事件之前修订的key的key-value对。为了节省带宽,只有在watch明确启用它时才会填写。
3.2????Watch streams
Watch是长时间运行的请求,并使用gRPC流来传输事件数据。 Watch streams 是双向的;?客户端写入流以建立监视和读取以接收监视事件。单个监视流可以通过使用每个监视标识符标记事件来复用许多不同的监视。这种多路复用有助于减少核心etcd集群的内存占用和连接开销。
Watch对事件做出三点保证:
有序 --- 事件按修订排序;?如果事件发生在已经发布的事件之前,则该事件将永远不会出现在watch上。
可靠 --- 一系列事件永远不会丢失任何事件的后续序列;?如果有事件按时间顺序排列为
原子 --- 事件清单保证包含完整的修订;?多个key上相同修订版的更新不会分成几个事件列表。
3.3? ? WatchCreateRequest
Key,Range_End --- 要watch的key范围。
Start_Revision --- 包含开始watch的可选修订版。如果没有给出,它将在修改监视创建响应标题修订版之后流式传输事件。可以从最后一次压缩修订开始watch整个可用事件历史记录。
Progress_Notify --- 设置后,如果没有最近的事件,watch将定期收到没有事件的WatchResponse。当客户希望从最近的已知修订版本开始恢复断开连接的watcher时,它非常有用。etcd服务器根据当前服务器负载决定发送通知的频率。
Fliters --- 要在服务器端过滤掉的事件类型列表。
Prev_Kv --- 设置后,watch会在事件发生之前接收key-value数据。这对于了解已覆盖的数据非常有用。
3.5????WatchCreateResponse:
Watch_ID --- 与响应对应的监视的ID。
Created --- 如果响应是针对创建监视请求,则设置为true。客户端应记录ID并期望在流上接收监视事件。发送给创建的watcher的所有事件都将具有相同的watch_id。
Canceled --- 如果响应是取消watch请求,则设置为true。不会向已取消的watcher发送更多事件。
Compact_Revision --- 如果watcher尝试watch压缩版本,则设置为etcd可用的最小历史版本。在压缩版本中创建watch程序或watcher无法跟上key-value存储的进度时会发生这种情况。watcher将被取消;?使用相同的start_revision创建新watch将失败。
Events - 与给定监视ID对应的顺序新事件列表。
3.6? ? WatchCancelRequest:
Watch_ID - 要取消的watch的ID,以便不再传输任何事件。
Lease是一种检测客户活跃度的机制。集群授予Lease生存时间。如果etcd集群在给定的TTL周期内没有收到keepAlive,则Lease到期。
为了将Lease绑定到key-value存储中,每个key可以附加到最多一个Lease。当Lease到期或被撤销时,附加到该Lease的所有key都将被删除。每个过期的key在事件历史记录中生成删除事件。
4.1???LeaseGrantRequest (?获得Lease)
TTL --- 咨询生存时间,以秒为单位。
ID --- 请求的Lease?ID。如果ID设置为0,则etcd将选择一个ID。
4.2????LeaseGrantResponse
ID - 授予的LeaseID。
TTL - 服务器为Lease选择的生存时间(以秒为单位)。
4.3????LeaseRevokeRequest
ID --- 要撤消的LeaseID。撤销Lease后,将删除所有附加的key。
4.4????LeaseRevokeResponse
4.5? ? LeaseKeepAliveRequest
使用通过LeaseKeepAliveAPI调用创建的双向流刷新Lease。当客户希望刷新Lease时,它会通过LeaseKeepAliveRequest流发送:
ID - 要保持活动的Lease的ID。
4.6????LeaseKeepAliveResponse
ID - 使用新TTL刷新的Lease。
TTL - Lease剩余的新生存时间(以秒为单位)。
gRPC笔记与相关问题
为什么要用RPC,数据编码,请求映射?
数据编码就是序列化,反序列化,将对象变成字节流发送给服务端,服务端将收到的字节流再转化为对象
常见的序列化,反序列化工具XML, JSON, Protobuf。
既然Protobuf在某些场景下效率要比JSON高,为什么高?
Json的缺点就是非字符串的编码效率低,int类型在内存只占两个字节65535, 转化成JSON却需要五个字节,bool则需要占用四到五个字节
另一个缺点就是信息冗余,面对同一个接口同一个对象,需要重复传送相同的字段名。
Json在编码效率和可读性之间选择了可读性
Protobuf选用了VarInts对数字进行编码,解决了效率问题,另一方面将字段都指定为整数编号,传输的时候只传送字段编号,解决了冗余问题,Protobuf使用.proto文件作为schema记录字段和编号的对应关系
gRPC底层使用的HTTP/2协议
HTTP协议本身可以通过Content-Encoding表示压缩算法,使用Contetn-length指定数据长度。
而gRPC重新定义了一套机制,因为gRPC支持stream rpc,流式接口。
gRPC支持三种流式接口,请求流,响应流,双向流。
请求流可以在RPC发起之后不断发送新的请求消息,此类接口最典型的使用场景事发推送或者短信。
相应流可以在RPC发起之后不断接受新的响应消息,最典型的场景就是订阅消息通知
双向流可以在RPC发起之后同时手法消息,最典型的场景就是实时语音转字幕
为了实现流式传输,gRPC引入Length-Prefixed Message同一个gRPC请求的不同消息共用HTTP头信息,只能给每个消息单独加一个五字节的前缀来表示压缩和长度信息。gRPC还定义了自己的grpc-status和grpc-message
HTTP/1.1也是支持复用TCP连接的,但这种复用有一个明显的缺陷,所有请求必须排队,先到先服务。
HTTP/2引入了stream的概念,解决了TCP链接复用的问题,可以在一条TCP连接上并行收发HTTP消息,而无需等待。
即gRPC为了实现流式特性,使用了HTTP/2进行通信。