ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

Dapr 内部 API(dapr/proto/internals/v1)协议深度解析:sidecar 间通信的 gRPC 契约与 Proto 客户端生成指南

2026/9/10 22:41:49 拓冰建站 浏览量
Dapr 内部 API(dapr/proto/internals/v1)协议深度解析:sidecar 间通信的 gRPC 契约与 Proto 客户端生成指南 Dapr 内部 APIdapr/proto/internals/v1协议深度解析sidecar 间通信的 gRPC 契约与 Proto 客户端生成指南【免费下载链接】daprDapr is a portable runtime for building distributed applications across cloud and edge, combining event-driven architecture with workflow orchestration.项目地址: https://gitcode.com/GitHub_Trending/da/dapr本文以 Dapr 仓库中的 dapr/proto/internals/v1/README.md 为骨架完整解析 Dapr 内部 API 协议包的定位、五个 .proto 文件的契约细节服务调用、Actor 提醒、Job 调度并逐条还原官方推荐的 Proto 客户端生成流程make init-proto/make gen-proto。读完本文你将能够理解daprdsidecar 之间零 HTTP 中间层的直连通信模型掌握从.proto到pkg/proto生成代码的完整工具链并能在源码中定位这些内部 RPC 的实际调用点。Internal APIs 在 Dapr 协议体系中的定位Dapr 仓库将全部 Protobuf 定义按职责划分为若干包dapr/README.md 中的总览表给出了它们的定位packagesdescriptioncommoncommon protos that are imported by multiple packagesinternalsinternal gRPC and protobuf definitions which is used for Dapr internalruntimeDapr and App Callback services and its associated protobuf messagesoperatorDapr Operator gRPC serviceplacementDapr Placement servicesentryDapr Sentry for CA servicecomponentsDapr gRPC-based components servicesinternals包正是本文的主角。dapr/proto/internals/v1/README.md 第一句话就给出了它的使命This folder is intended for the Internal APIs that thedaprdsidecars use to communicate with each other.也就是说这里的协议不面向用户应用而是用于 Dapr sidecardaprd之间的内部互通。典型场景是服务 A 的 sidecar 在收到用户对服务 B 的调用请求后需要把请求转交给服务 B 的 sidecar——这条 sidecar-to-sidecar 链路走的就是internals包定义的 gRPC 服务ServiceInvocation。该目录下共包含 5 个.proto文件覆盖四类内部能力service_invocation.protosidecar 间的服务调用与 Actor 调用2021 年引入最核心apiversion.proto内部 API 版本枚举status.protoHTTP/gRPC 应用通道的响应状态reminders.protoActor 提醒/定时器事件2023 年引入jobs.protoJob 调度请求与事件2024 年引入。所有文件统一使用syntax proto3包名为dapr.proto.internals.v1并声明 Go 包映射github.com/dapr/dapr/pkg/proto/internals/v1;internals——这正是生成代码落盘位置的依据。ServiceInvocationsidecar 间服务调用的核心契约RPC 方法总览service_invocation.proto 定义了唯一的 gRPC 服务ServiceInvocation第 36–60 行包含 5 个 RPC 方法RPC 方法类型用途CallActorUnary调用指定 Actor 的方法CallLocalUnary调用指定服务应用的方法CallActorReminderUnary触发远程内部 Actor 的 ReminderCallLocalStreamBidi-streaming以数据流方式调用服务方法请求分块上行、响应分块下行CallActorStreamServer-streaming调用 Actor 方法并流式返回响应.proto注释中有一个非常关键的设计说明这些 RPC 的 gRPC 响应状态只代表内部连接状态不代表被调用方的业务响应状态。也就是说CallLocal/CallActor的 gRPC 层在大多数情况下都返回OK真正的 HTTP 状态码/业务错误被封装在InternalInvokeResponse.status字段中带回调用方由调用方 sidecar 再翻译成对用户请求的响应。这一点从消息结构上可以看得很清楚见下文Status消息。核心消息结构InternalInvokeRequest第 73–86 行是调用方 sidecar 向被调用方 sidecar 传输的完整请求载体message InternalInvokeRequest { APIVersion ver 1; // 必填Dapr Runtime API 版本 mapstring, ListStringValue metadata 2; // 必填调用方的 HTTP header 或 gRPC metadata common.v1.InvokeRequest message 3; // 必填调用方的调用请求 Actor actor 4; // 仅 Actor 调用时使用 }InternalInvokeResponse第 90–103 行则反向携带被调用方的响应message InternalInvokeResponse { Status status 1; // 必填HTTP/gRPC 状态 mapstring, ListStringValue headers 2; // 必填应用回调的响应头 mapstring, ListStringValue trailers 3; // 仅 gRPC 应用回调使用 common.v1.InvokeResponse message 4; // 被调用方的响应消息 }两个辅助消息也很常用Actor第 63–69 行由actor_type必填与actor_id必填唯一标识一个 Actor 实例。在 pkg/proto/internals/v1/service_invocation_additional.go 中手写辅助方法GetActorKey()用||分隔符把二者拼成 Actor 的 FQDN keyactor_type || actor_id这个 key 会被 Reminder、定时器等模块复用。ListStringValue第 126–129 行repeated string values用于承载可重复的 header/metadata 值。流式 RPC 的分块语义CallLocalStream是CallLocal的数据流版本。虽然它声明为双向流但.proto注释明确要求其行为等价于简单 RPC调用方先发送完整请求按块分多条消息再读取完整响应同样按块分条。协议对消息编排有严格要求流中第一条消息必须携带request调用方或response被调用方且所有必填属性齐全该消息可以携带一个payload也可以为空后续所有消息只能携带payload不得再出现request/response等其他属性携带payload的每条消息必须带序号seq从 0 开始、每个分块递增 1不带payload的消息不得出现seq发送方发完数据后必须调用CloseSend每个方向上至少要发送一条消息若只发一条则该消息必须同时携带request/responsepayload允许为空。对应的流消息定义第 106–123 行在结构上刻意与 Unary 版本错开InternalInvokeRequestStream内部分别持有InternalInvokeRequest request其message.data不携带数据和common.v1.StreamPayload payload数据分块InternalInvokeResponseStream同理。从源码可以看到该协议在流式调用时的工程约束pkg/messaging/v1/util.go 中注释说明CallLocalStream的缓冲上限为 2KBpkg/messaging/direct_messaging.go 发起CallLocalStream调用并在 第 489 行附近 对对端 sidecar 不支持CallLocalStream返回Unimplemented做了降级回退处理——这是新旧 sidecar 混布版本倾斜场景下的兼容性设计。单测 pkg/messaging/direct_messaging_test.go 中还分别 mock 了CallLocalStream的客户端与服务端流验证了这套流式消息编排。三个轻量辅助协议版本、状态与事件APIVersion内部 API 的版本门禁apiversion.proto第 21–27 行只定义了一个枚举enum APIVersion { APIVERSION_UNSPECIFIED 0; // 未指定 V1 1; // Dapr API v1 }InternalInvokeRequest.ver字段使用该枚举保证调用双方 sidecar 对内部 API 版本的语义一致。目前只有V1一个正式版本。Status业务状态与 gRPC 状态解耦status.proto第 23–32 行定义了Statusmessage Status { int32 code 1; // 必填状态码 string message 2; // 错误信息 repeated google.protobuf.Any details 3; // 错误详情列表 }该消息承载的是被调用方应用的响应状态HTTP 状态码或 gRPC 状态与ServiceInvocation服务自身的 gRPC 连接状态相互独立——这正是前文内部 RPC 大多返回 OK设计落地的关键。Reminder 与 TimerFiredEventActor 提醒/定时器事件reminders.proto2023 年引入定义了存储于 Dapr Actor 状态存储中的提醒结构message Reminder { string actor_id 1; string actor_type 2; string name 3; google.protobuf.Any data 4; string period 5; google.protobuf.Timestamp registered_time 6; string due_time 7; google.protobuf.Timestamp expiration_time 8; bool is_timer 9; bool skip_lock 10; } message Reminders { repeated Reminder reminders 1; } message TimerFiredEvent { google.protobuf.Timestamp fire_at 1; int32 timerId 2; uint64 generation 3; }Reminder统一了提醒reminder与定时器timer两种模型is_timer区分并支持周期period、到期时间expiration_time与锁跳过skip_lock。TimerFiredEvent描述了定时器触发时发送给 Actor 的事件含触发时刻、timerId 与代际generation。在源码中Reminder会被直接作为远程 RPC 的入参调用方侧 pkg/actors/router/router.go 通过CallActorReminder(ctx, internalv1pb.Reminder{...})触发对端 sidecar 的 Actor 提醒接收方侧 pkg/api/grpc/daprinternal.go 的CallActorReminder实现会校验actor_type、actor_id并交由 Actor 运行时投递。JobHTTPRequest 与 JobEventJob 调度协议jobs.proto2024 年引入定义了两个消息// 用于 HTTP Dapr Job API保证 data 始终以 JSON 对象google.protobuf.Struct序列化 message JobHTTPRequest { string name 1 [json_name name]; optional string schedule 2 [json_name schedule]; optional uint32 repeats 3 [json_name repeats]; optional string due_time 4 [json_name dueTime]; optional string ttl 5 [json_name ttl]; google.protobuf.Value data 6 [json_name data]; optional bool overwrite 7 [json_name overwrite]; optional common.v1.JobFailurePolicy failure_policy 8 [json_name failurePolicy]; } message JobEvent { string key 1; // Job 的 FQDN key string name 2; // Job 名称 scheduler.v1.JobMetadata metadata 3; google.protobuf.Any data 4; }JobHTTPRequest的注释特意说明其字段文档以dapr/proto/runtime/v1/dapr.proto中的对应定义为准而之所以在内部包中单独定义一份是为了保证 HTTP 侧data始终以 JSON 对象google.protobuf.Struct形态序列化。该消息被 HTTP API 层直接复用pkg/api/http/jobs.go 将JobHTTPRequest作为 HTTP 处理器与 Universal 层之间的请求载体。JobEvent则是 Job 到期时交由 Scheduler 处理的事件携带 FQDN key、元数据与数据负载。Proto 客户端生成从 .proto 到 pkg/protoREADME 给出了完整的生成流程核心是两条make命令。下面是逐步拆解并与当前仓库 Makefile 的实际实现做了对齐。步骤 1安装 protoc按 README 要求首先需要安装指定版本的 protocInstall protoc version: v4.25.4即protobuf v4.25.4对应的 protoc 编译器。需要特别说明版本一致性当前仓库 Makefile 中实际固定的是PROTOC_VERSION 34.1PROTOBUF_SUITE_VERSION 34.1而gen-proto依赖的check-proto-version目标Makefile会严格校验protoc --version输出是否为libprotoc 34.1版本不匹配会直接报错退出。因此如果你使用较新版本的 Dapr 源码建议以 Makefile 中PROTOC_VERSION/PROTOC_GEN_*_VERSION固定的版本为准README 中的 v4.25.4 是编写该文档时的基线。dapr/README.md还提供了 WindowsWSL2 Ubuntu 24.04下通过wget下载 protoc 压缩包、解压并放入/usr/local/bin的替代安装步骤。步骤 2安装生成插件make init-protoinit-proto目标Makefile通过go install安装三个官方插件init-proto: go install google.golang.org/protobuf/cmd/protoc-gen-go$(PROTOC_GEN_GO_VERSION) go install google.golang.org/grpc/cmd/protoc-gen-go-grpcv$(PROTOC_GEN_GO_GRPC_VERSION) go install connectrpc.com/connect/cmd/protoc-gen-connect-gov$(PROTOC_GEN_CONNECT_GO_VERSION)当前仓库固定版本为protoc-gen-go v1.32.0、protoc-gen-go-grpc v1.3.0、protoc-gen-connect-go v1.18.1见 Makefile。三个插件各司其职protoc-gen-go生成消息的 Go 结构体*.pb.goprotoc-gen-go-grpc生成 gRPC 服务代码*_grpc.pb.goprotoc-gen-connect-go生成 Connect RPC 客户端/服务端代码*connect/目录。步骤 3生成 gRPC Proto 客户端在仓库根目录执行make gen-protogen-proto目标Makefile先运行check-proto-version校验工具链版本然后为dapr/proto下的每个子包GRPC_PROTOS即 operator、placement、sentry、common、runtime、internals、components 等逐一执行生成。每个包的实际 protoc 命令模板Makefile为protoc -I . ./dapr/proto/$(1)/v1/*.proto \ --go_out. --go_optmodulegithub.com/dapr/dapr \ --go-grpc_out. --go-grpc_optrequire_unimplemented_serversfalse,modulegithub.com/dapr/dapr \ --connect-go_out. --connect-go_optmodulegithub.com/dapr/dapr几个值得注意的选项modulegithub.com/dapr/dapr让生成文件按 Go module 路径布局直接落入仓库内的pkg/proto/...而不是产生github.com/dapr/dapr的冗余目录层级require_unimplemented_serversfalse生成的服务接口不强制内嵌UnimplementedXxxServer便于测试代码用轻量 mock 直接实现接口pkg/messaging/direct_messaging_test.go 中的mockGRPCServerUnary就是直接实现CallLocal的例子生成完毕后gen-proto还会执行modtidygo mod tidy保持依赖一致。步骤 4查看生成产物README 指出生成结果位于pkg/proto。对 internals 包而言生成产物在 pkg/proto/internals/v1 下pkg/proto/internals/v1/ ├── apiversion.pb.go ├── jobs.pb.go ├── reminders.pb.go ├── service_invocation.pb.go ├── service_invocation_grpc.pb.go ├── service_invocation_additional.go # 手写扩展非生成 ├── service_invocation_additional_test.go # 手写扩展的单元测试 ├── status.pb.go └── internalsconnect/ └── service_invocation.connect.go # Connect RPC 生成代码其中service_invocation_additional.go是人工编写的辅助代码文件头注释明确说明contains additional, hand-written methods added to the generated objects定义了GRPCContentType、JSONContentType、ProtobufContentType、OctetStreamContentType等媒体类型常量第 25–34 行以及Actor.GetActorKey()、NewInternalInvokeRequest(method)等便捷构造器第 46–60 行。NewInternalInvokeRequest会自动填充Ver: APIVersion_V1并初始化common.v1.InvokeRequest{Method: method}是调用方 sidecar 构造内部请求的标准入口。在源码中验证内部 RPC 的真实调用链生成代码最终服务于 sidecar 的运行时逻辑以下是三条可以对照阅读的核心调用链服务调用的主路径pkg/messaging/direct_messaging.go 是direct messaging的枢纽。Unary 调用在 第 385 行 通过clientV1.CallLocal(ctx, pd, opts...)把用户请求转发给目标应用的 sidecar需要传输大请求时则切换为 第 400 行 的CallLocalStream。配合 pkg/messaging/v1/util.go 的 2KB 缓冲注释可以看到流式调用专为突破 Unary 消息大小限制而设计。Actor 提醒的远程触发pkg/actors/router/router.go 在确认目标 Actor 不在本地后构造internalv1pb.Reminder并调用client.CallActorReminder(...)对端 pkg/api/grpc/daprinternal.go 的CallActorReminder实现接收后投递给 Actor 运行时。这条链路完整复现了reminders.proto与service_invocation.proto的协作。Job 的 HTTP 入口pkg/api/http/jobs.go 直接把internalsv1pb.JobHTTPRequest作为 HTTP→Universal 层的中介类型印证了jobs.protoHTTP 侧 data 恒为 JSON 对象 的设计初衷而 dapr/proto/scheduler/v1/scheduler.proto 则是JobEvent.metadata中JobMetadata的来源二者共同构成Job API → 内部事件 → Scheduler的链路。小结与扩展阅读dapr/proto/internals/v1是 Dapr sidecar 之间不打用户 HTTP 网关的内部通信契约ServiceInvocation承载服务调用与 Actor 调用含流式分块Reminder/JobEvent承载 Actor 提醒与 Job 调度事件APIVersion与Status则保障版本语义与业务状态/连接状态解耦。配合make init-protomake gen-proto的工具链任何.proto修改都能稳定地同步到pkg/proto下的 Go 代码。如需继续深入推荐按以下路径阅读当前仓库协议定义继续阅读 dapr/proto/internals/v1/service_invocation.proto 与 dapr/proto/internals/v1/reminders.proto 的完整注释生成工具链Makefile 的init-proto/gen-proto/check-proto-version三个目标以及 tools/codegen.mk 中面向各 SDK 的旧版生成模板调用方实现pkg/messaging/direct_messaging.go 与 pkg/messaging/direct_messaging_test.go接收方实现pkg/api/grpc/daprinternal.go含CallActorReminder等内部 API 的落地跨包协议总览dapr/README.md含 Windows/WSL2 下的 protoc 安装变体与 e2e 测试应用依赖更新说明。【免费下载链接】daprDapr is a portable runtime for building distributed applications across cloud and edge, combining event-driven architecture with workflow orchestration.项目地址: https://gitcode.com/GitHub_Trending/da/dapr创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考