基于编译出的的go文件进行说明
远程执行的目的是通过两个独立但相关的概念:远程缓存和远程执行来加快构建过程。远程缓存允许用户共享构建输出,而远程执行则允许在远程机器集群上运行操作,这些机器集群可能比用户本地使用的机器集群功能更强大(或配置不同)。
远程执行应用程序接口(REAPI)描述了一个 gRPC + 协议缓冲区接口,为远程缓存和执行提供了三种主要服务:
- 内容可寻址存储(ContentAddressableStorage,CAS)服务:远程存储端点,通过摘要对内容进行寻址,摘要是存储或检索数据的哈希值和大小对。
- 动作缓存(AC)服务:已执行的构建动作与相应的结果工件之间的映射(通常与 CAS 服务共存)。
- 执行服务:允许用户请求针对构建农场执行构建任务的主要端点。
结构体
基础字段
re中每个表示message的结构体都有3个基础字段:
state:
由 Protocol Buffers 自动生成,用于管理消息的内部状态,通常用于支持序列化、反序列化和其他内部操作。
sizeCache:
由 Protocol Buffers 自动生成,用于缓存消息的大小信息,以优化序列化性能。
unknownFields:
由 Protocol Buffers 自动生成,用于存储解析过程中遇到的未知字段信息。这有助于在结构变化时保持兼容性。
摘要
摘要是re中最常见的结构体。用于对文件/action/command等实体进行唯一标识。
digest 包含哈希值和数据块大小来表示文件或数据块的摘要信息。在分布式系统中,可以进行高效的缓存、验证和去重操作。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
|
type Digest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields Hash string `protobuf:"bytes,1,opt,name=hash,proto3" json:"hash,omitempty"` SizeBytes int64 `protobuf:"varint,2,opt,name=size_bytes,json=sizeBytes,proto3" json:"size_bytes,omitempty"` }
|
文件
文件夹
文件夹Directory
,表示一个无名的目录,其中包含实际的子文件夹、文件和链接。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80
|
type Directory struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields Files []*FileNode `protobuf:"bytes,1,rep,name=files,proto3" json:"files,omitempty"` Directories []*DirectoryNode `protobuf:"bytes,2,rep,name=directories,proto3" json:"directories,omitempty"` Symlinks []*SymlinkNode `protobuf:"bytes,3,rep,name=symlinks,proto3" json:"symlinks,omitempty"` NodeProperties *NodeProperties `protobuf:"bytes,5,opt,name=node_properties,json=nodeProperties,proto3" json:"node_properties,omitempty"` }
|
值得注意的是,Directory自身并不是嵌套关系。比如上面代码注释中给出的例子,表示的是如下目录结构:
1 2 3 4 5
| / ├── bar (文件, 哈希值: 4a73bc9d03..., 大小: 65534, 节点属性: { MTime: 2017-01-15T01:30:15.01Z }) └── foo (目录, 哈希值: 4cf2eda940..., 大小: 43) └── baz (可执行文件, 哈希值: b2c941073e..., 大小: 1294)
|
根目录包含了foo目录,分别用两个独立的Directory
结构表示,嵌套关系则使用DirectoryNode
成员(其中只记录foo目录的摘要)表示。
Directory
不持有自身的名字和摘要。
这种办法适合于分布式文件存储系统(如IPFS),能通过摘要(哈希值)进行标识和检索数据。
文件夹节点/文件节点/链接节点
文件夹节点(DirectoryNode
)不同于文件夹(Directory
),只包含了用于在分布式文件系统中检索文件夹的必要数据。
1 2 3 4 5 6 7 8 9 10 11 12
|
type DirectoryNode struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` Digest *Digest `protobuf:"bytes,2,opt,name=digest,proto3" json:"digest,omitempty"` }
|
Digest
: 采用 Merkle Tree(默克尔树)结构生成,这种结构可以为整个目录树生成一个唯一的摘要,确保内容的一致性和完整性。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| type FileNode struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` Digest *Digest `protobuf:"bytes,2,opt,name=digest,proto3" json:"digest,omitempty"` IsExecutable bool `protobuf:"varint,4,opt,name=is_executable,json=isExecutable,proto3" json:"is_executable,omitempty"` NodeProperties *NodeProperties `protobuf:"bytes,6,opt,name=node_properties,json=nodeProperties,proto3" json:"node_properties,omitempty"` }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| type SymlinkNode struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` Target string `protobuf:"bytes,2,opt,name=target,proto3" json:"target,omitempty"` NodeProperties *NodeProperties `protobuf:"bytes,4,opt,name=node_properties,json=nodeProperties,proto3" json:"node_properties,omitempty"` }
|
FileNode
、SymlinkNode
和Directory
(而不是DirectoryNode
) 表示了文件/目录/链接在文件系统中的全部信息。
节点属性
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
|
type NodeProperties struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields Properties []*NodeProperty `protobuf:"bytes,1,rep,name=properties,proto3" json:"properties,omitempty"` Mtime *timestamp.Timestamp `protobuf:"bytes,2,opt,name=mtime,proto3" json:"mtime,omitempty"` UnixMode *wrappers.UInt32Value `protobuf:"bytes,3,opt,name=unix_mode,json=unixMode,proto3" json:"unix_mode,omitempty"` }
|
DirecotoyNode
实际上并没有节点属性的字段。文件夹对应的节点属性位于其摘要对应的Direcory
结构中。
NodeProperty
是一个键值对。
1 2 3 4 5 6 7 8 9 10
| type NodeProperty struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` Value string `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"` }
|
指令
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102
|
type Command struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields Arguments []string `protobuf:"bytes,1,rep,name=arguments,proto3" json:"arguments,omitempty"` EnvironmentVariables []*Command_EnvironmentVariable `protobuf:"bytes,2,rep,name=environment_variables,json=environmentVariables,proto3" json:"environment_variables,omitempty"` OutputFiles []string `protobuf:"bytes,3,rep,name=output_files,json=outputFiles,proto3" json:"output_files,omitempty"` OutputDirectories []string `protobuf:"bytes,4,rep,name=output_directories,json=outputDirectories,proto3" json:"output_directories,omitempty"` OutputPaths []string `protobuf:"bytes,7,rep,name=output_paths,json=outputPaths,proto3" json:"output_paths,omitempty"` Platform *Platform `protobuf:"bytes,5,opt,name=platform,proto3" json:"platform,omitempty"` WorkingDirectory string `protobuf:"bytes,6,opt,name=working_directory,json=workingDirectory,proto3" json:"working_directory,omitempty"` OutputNodeProperties []string `protobuf:"bytes,8,rep,name=output_node_properties,json=outputNodeProperties,proto3" json:"output_node_properties,omitempty"` }
|
EnvironmentVariables
: 运行命令时设置的环境变量。如:["PATH": "/usr/bin/:/usr/local/bin"]
。
OutputPaths
: 客户端期望从动作中检索的输出路径列表。路径相对于动作执行的工作目录。如["User/buttering/pcc/pcc/cmd/cc/a.out"]
。
WorkingDirectory
: 命令运行时的工作目录,相对于输入根。
OutputNodeProperties
: 客户端期望从输出文件和目录中检索的节点属性键列表。
Command
不持有自身的摘要。
动作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62
|
type Action struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields
CommandDigest *Digest `protobuf:"bytes,1,opt,name=command_digest,json=commandDigest,proto3" json:"command_digest,omitempty"`
InputRootDigest *Digest `protobuf:"bytes,2,opt,name=input_root_digest,json=inputRootDigest,proto3" json:"input_root_digest,omitempty"`
Timeout *duration.Duration `protobuf:"bytes,6,opt,name=timeout,proto3" json:"timeout,omitempty"`
DoNotCache bool `protobuf:"varint,7,opt,name=do_not_cache,json=doNotCache,proto3" json:"do_not_cache,omitempty"`
Salt []byte `protobuf:"bytes,9,opt,name=salt,proto3" json:"salt,omitempty"`
Platform *Platform `protobuf:"bytes,10,opt,name=platform,proto3" json:"platform,omitempty"` }
|
CommandDigest
: 要执行的命令的摘要,该命令必须存在于内容可寻址存储(CAS)中。
InputRootDigest
: 输入文件的根目录的摘要,这些文件在命令执行之前会被放在构建机器上的正确位置。
Timeout
: 执行应在其后被终止的超时时间。如果客户端未指定超时,服务器应强制执行超时。不同的超时时间的Action,其摘要也不同。
为什么超时会(要)导致摘要不同?
在远程执行系统中,Action 的摘要不仅依赖于包含的文件和命令,还依赖于其所有元数据,包括超时时间。这样一来,即使命令和文件完全相同,但如果超时不同,摘要就会不同。
假设有两个相同的 Action,但是一个有较短的超时,另一个有较长的超时。如果这两个 Action共享同一个摘要,系统可能会以为它们是完全相同的操作,并使用缓存中的结果。但是,如果较短超时的 Action 实际上需要更长时间来完成,并且它因超时而失败,那么这次执行结果将被错误地缓存,并在未来的请求中被使用,从而导致不一致且不可预料的行为。
action
同样不持有自身的摘要。
平台信息
1 2 3 4 5 6 7 8 9 10 11 12
|
type Platform struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields Properties []*Platform_Property `protobuf:"bytes,1,rep,name=properties,proto3" json:"properties,omitempty"` }
|
Platform_Property
表示一个键值对:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
|
type Platform_Property struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` Value string `protobuf:"bytes,2,opt,name=value,proto3" json:"value,omitempty"` }
|
请求
请求元数据
请求元数据是 gRPC 协议的一部分,作为gRPC请求的元数据携带。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
|
type RequestMetadata struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields ToolDetails *ToolDetails `protobuf:"bytes,1,opt,name=tool_details,json=toolDetails,proto3" json:"tool_details,omitempty"` ActionId string `protobuf:"bytes,2,opt,name=action_id,json=actionId,proto3" json:"action_id,omitempty"` ToolInvocationId string `protobuf:"bytes,3,opt,name=tool_invocation_id,json=toolInvocationId,proto3" json:"tool_invocation_id,omitempty"` CorrelatedInvocationsId string `protobuf:"bytes,4,opt,name=correlated_invocations_id,json=correlatedInvocationsId,proto3" json:"correlated_invocations_id,omitempty"` ActionMnemonic string `protobuf:"bytes,5,opt,name=action_mnemonic,json=actionMnemonic,proto3" json:"action_mnemonic,omitempty"` TargetId string `protobuf:"bytes,6,opt,name=target_id,json=targetId,proto3" json:"target_id,omitempty"` ConfigurationId string `protobuf:"bytes,7,opt,name=configuration_id,json=configurationId,proto3" json:"configuration_id,omitempty"` }
|
服务器使用metadata.FromIncomingContext(ctx context.Context)
进行拦截;
客户端使用metadata.NewOutgoingContext(ctx context.Context, md MD)
将元数据添加到请求中。
执行请求
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
| type ExecuteRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields InstanceName string `protobuf:"bytes,1,opt,name=instance_name,json=instanceName,proto3" json:"instance_name,omitempty"` SkipCacheLookup bool `protobuf:"varint,3,opt,name=skip_cache_lookup,json=skipCacheLookup,proto3" json:"skip_cache_lookup,omitempty"` ActionDigest *Digest `protobuf:"bytes,6,opt,name=action_digest,json=actionDigest,proto3" json:"action_digest,omitempty"` ExecutionPolicy *ExecutionPolicy `protobuf:"bytes,7,opt,name=execution_policy,json=executionPolicy,proto3" json:"execution_policy,omitempty"` ResultsCachePolicy *ResultsCachePolicy `protobuf:"bytes,8,opt,name=results_cache_policy,json=resultsCachePolicy,proto3" json:"results_cache_policy,omitempty"` DigestFunction DigestFunction_Value `protobuf:"varint,9,opt,name=digest_function,json=digestFunction,proto3,enum=build.bazel.remote.execution.v2.DigestFunction_Value" json:"digest_function,omitempty"` }
|
批量上传
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
|
type BatchUpdateBlobsRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields InstanceName string `protobuf:"bytes,1,opt,name=instance_name,json=instanceName,proto3" json:"instance_name,omitempty"` Requests []*BatchUpdateBlobsRequest_Request `protobuf:"bytes,2,rep,name=requests,proto3" json:"requests,omitempty"` DigestFunction DigestFunction_Value `protobuf:"varint,5,opt,name=digest_function,json=digestFunction,proto3,enum=build.bazel.remote.execution.v2.DigestFunction_Value" json:"digest_function,omitempty"` }
type BatchUpdateBlobsRequest_Request struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields Digest *Digest `protobuf:"bytes,1,opt,name=digest,proto3" json:"digest,omitempty"` Data []byte `protobuf:"bytes,2,opt,name=data,proto3" json:"data,omitempty"` Compressor Compressor_Value `protobuf:"varint,3,opt,name=compressor,proto3,enum=build.bazel.remote.execution.v2.Compressor_Value" json:"compressor,omitempty"` }
|
响应
相关结构体
操作
首先介绍Operation消息,被定义在google.longrunning.Operation API中。可以用Operation于跟踪和管理长时间运行的操作状态和结果。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| type Operation struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` Metadata *anypb.Any `protobuf:"bytes,2,opt,name=metadata,proto3" json:"metadata,omitempty"` Done bool `protobuf:"varint,3,opt,name=done,proto3" json:"done,omitempty"` Result isOperation_Result `protobuf_oneof:"result"` }
|
长时操作的API请求的返回值都会被包裹在Operation
中。
当请求完成且成功时,Result
字段为Operation_Response
:
1 2 3 4 5 6
| type Operation_Response struct { Response *anypb.Any `protobuf:"bytes,5,opt,name=response,proto3,oneof"` }
|
例如,Execute在生成响应时,会将**Operation.Result.Response**
赋为**ExecuteResponse**
,将**Operation.Metadata**
赋为**ExecuteOperationMetadata**
。
状态
Status
用于记录操作是否成功等信息。定义在googleapi.rpc中。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
|
type Status struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields Code int32 `protobuf:"varint,1,opt,name=code,proto3" json:"code,omitempty"` Message string `protobuf:"bytes,2,opt,name=message,proto3" json:"message,omitempty"` Details []*anypb.Any `protobuf:"bytes,3,rep,name=details,proto3" json:"details,omitempty"` }
|
状态码列举如下:
- OK (Code = 0):表示操作成功。
- Canceled (Code = 1):表示操作被取消(通常是由调用方发起)。
- Unknown (Code = 2):表示未知错误,通常在接收到未知错误空间的 Status 值时返回。
- InvalidArgument (Code = 3):表示客户端指定了无效的参数,例如格式错误的文件名。
- DeadlineExceeded (Code = 4):表示操作在完成之前已过期。
- NotFound (Code = 5):表示请求的实体(如文件或目录)未找到。
- AlreadyExists (Code = 6):表示试图创建的实体已经存在。
- PermissionDenied (Code = 7):表示调用方没有执行指定操作的权限。
- ResourceExhausted (Code = 8):表示某些资源已耗尽,例如用户配额或文件系统空间不足。
- FailedPrecondition (Code = 9):表示操作被拒绝,因为系统不处于执行操作所需的状态。
- Aborted (Code = 10):表示操作被中止,通常由于并发问题。
- OutOfRange (Code = 11):表示操作超出有效范围,如搜索或读取超出文件末尾。
- Unimplemented (Code = 12):表示操作未实现或在服务中不受支持。
- Internal (Code = 13):表示内部错误,系统预期的不变量被破坏。
- Unavailable (Code = 14):表示服务当前不可用,可能是暂时的。
- DataLoss (Code = 15):表示数据丢失或损坏不可恢复。
- Unauthenticated (Code = 16):表示请求没有有效的认证凭证来执行操作。
执行
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28
|
type ExecuteResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields
Result *ActionResult `protobuf:"bytes,1,opt,name=result,proto3" json:"result,omitempty"` CachedResult bool `protobuf:"varint,2,opt,name=cached_result,json=cachedResult,proto3" json:"cached_result,omitempty"` Status *status.Status `protobuf:"bytes,3,opt,name=status,proto3" json:"status,omitempty"` ServerLogs map[string]*LogFile `protobuf:"bytes,4,rep,name=server_logs,json=serverLogs,proto3" json:"server_logs,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` Message string `protobuf:"bytes,5,opt,name=message,proto3" json:"message,omitempty"` }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
|
type ExecuteOperationMetadata struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields Stage ExecutionStage_Value `protobuf:"varint,1,opt,name=stage,proto3,enum=build.bazel.remote.execution.v2.ExecutionStage_Value" json:"stage,omitempty"` ActionDigest *Digest `protobuf:"bytes,2,opt,name=action_digest,json=actionDigest,proto3" json:"action_digest,omitempty"` StdoutStreamName string `protobuf:"bytes,3,opt,name=stdout_stream_name,json=stdoutStreamName,proto3" json:"stdout_stream_name,omitempty"` StderrStreamName string `protobuf:"bytes,4,opt,name=stderr_stream_name,json=stderrStreamName,proto3" json:"stderr_stream_name,omitempty"` PartialExecutionMetadata *ExecutedActionMetadata `protobuf:"bytes,5,opt,name=partial_execution_metadata,json=partialExecutionMetadata,proto3" json:"partial_execution_metadata,omitempty"` }
|
批量上传
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
|
type BatchUpdateBlobsResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields Responses []*BatchUpdateBlobsResponse_Response `protobuf:"bytes,1,rep,name=responses,proto3" json:"responses,omitempty"` }
type BatchUpdateBlobsResponse_Response struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields Digest *Digest `protobuf:"bytes,1,opt,name=digest,proto3" json:"digest,omitempty"` Status *status.Status `protobuf:"bytes,2,opt,name=status,proto3" json:"status,omitempty"` }
|
接口
执行
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
| type ExecutionServer interface { Execute(*ExecuteRequest, Execution_ExecuteServer) error
WaitExecution(*WaitExecutionRequest, Execution_WaitExecutionServer) error }
|
Execute向客户端返回的是一个google.longrunning.Operation
结构。
1 2 3
| rpc Execute(ExecuteRequest) returns (stream google.longrunning.Operation) { option (google.api.http) = { post: "/v2/{instance_name=**}/actions:execute" body: "*" }; }
|
内容寻址存储
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67
| type ContentAddressableStorageServer interface { FindMissingBlobs(context.Context, *FindMissingBlobsRequest) (*FindMissingBlobsResponse, error)
BatchUpdateBlobs(context.Context, *BatchUpdateBlobsRequest) (*BatchUpdateBlobsResponse, error)
BatchReadBlobs(context.Context, *BatchReadBlobsRequest) (*BatchReadBlobsResponse, error)
GetTree(*GetTreeRequest, ContentAddressableStorage_GetTreeServer) error }
|
gRPC心跳
在 gRPC (Google Remote Procedure Call) 中,长连接的心跳机制是由 gRPC 底层库自动处理的。具体来说,gRPC 使用 HTTP/2 协议,该协议内置了持久连接和心跳机制。HTTP/2 协议本身支持长连接模式,并通过设置 PING 帧来进行连接的健康检查和保持活跃。
在使用 gRPC 时:
- 长连接:
gRPC 客户端与服务器之间建立的连接是长连接,默认情况下会一直保持开启,除非客户端或服务器主动关闭连接。
- 自动心跳:
gRPC 基于 HTTP/2 的连接层心跳机制,HTTP/2 会定期发送 PING 帧来检查连接的健康状况,确保连接是活跃的,且能够检测到潜在的连接中断。
- 超时和重试:
连接超时和故障恢复机制是 gRPC 内置的特性,当检测到连接断开或无法响应时,gRPC 客户端会尝试自动重连。
gRPC消息大小限制
gRPC 使用 Protocol Buffers(protobuf)来定义消息结构。一个 protobuf 消息可能包含多个字段(不只是data),而 gRPC 在传输时将整个消息序列化为一个字节流。
grpc.MaxRecvMsgSize 和 grpc.MaxSendMsgSize 用于配置选项限制的是客户端或服务器在单个 RPC 调用中发送或接收的最大消息大小。默认消息大小为4MB。