ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

MongoDB 仓库内嵌 gRPC C++ Deadline 示例深度解析:超时控制与 deadline 传播实战

MongoDB 仓库内嵌 gRPC C++ Deadline 示例深度解析:超时控制与 deadline 传播实战 MongoDB 仓库内嵌 gRPC C Deadline 示例深度解析超时控制与 deadline 传播实战【免费下载链接】mongoThe MongoDB Database项目地址: https://gitcode.com/GitHub_Trending/mo/mongo导读本篇文章以 MongoDB 仓库 vendored 的 gRPC 发行版位于src/third_party/grpc/dist中官方 C Deadline 示例为骨架系统讲解 gRPC 调用中 deadline截止时间的用法如何在客户端为一元 RPC 设置超时、服务端如何通过响应延迟人为制造超时场景以及 deadline 如何沿服务调用链自动传播。读完本文你将掌握ClientContext::set_deadline、ClientContext::FromCallbackServerContext这两个核心 API 的实战用法并理解端到端 deadline这一分布式系统可靠性设计的关键机制。一、示例概览用 4 个 RPC 讲清 deadlinegRPC 的 deadline 机制用于给一次 RPC 调用设置最晚完成时间一旦超过该时间调用方将收到DEADLINE_EXCEEDED错误从而避免调用无限期挂起。官方示例位于 deadline 示例目录由三部分构成文件职责README.md示例说明与运行步骤client.cc客户端设置 1 秒 deadline依次发起 4 个测试调用server.cc服务端实现响应延迟与 deadline 传播两类测试场景该示例基于helloworld原型服务其服务定义见 helloworld.protoGreeter服务暴露SayHello一元 RPC请求为HelloRequest{name}响应为HelloReply{message}。整个示例围绕 4 个测试调用展开分别验证 4 种场景正常请求在 deadline 内完成请求被服务端故意延迟触发客户端DEADLINE_EXCEEDED带[propagate me]前缀的请求触发服务端自我转发验证 deadline 在调用链上传播后仍能正常完成带两个[propagate me]前缀的请求经过两级转发验证传播后的 deadline 同样生效超时。二、构建与运行Bazel 与 CMake 两种方式README 明确说明只要 gRPC 构建可用就可以用 bazel 或 cmake 构建本示例。2.1 Bazel 构建Bazel 构建文件 中定义了client与server两个cc_binary目标二者均依赖//:grpcgRPC C 核心库、//examples/protos:helloworld_cc_grpc由 proto 生成的 gRPC 桩代码以及 absl 的 flags/log 库。可以推断在 gRPC 源码树根目录下可分别构建两个目标bazel build //examples/cpp/deadline:client bazel build //examples/cpp/deadline:serverBAZEL_BUILD宏由构建文件通过defines [BAZEL_BUILD]注入用于让源码区分 include 路径见下文源码解析。2.2 CMake 构建CMake 构建由 common.cmake 统一驱动该文件支持三种依赖获取策略GRPC_AS_SUBMODULE通过add_subdirectory(../../.. ...)把 gRPC 源码树直接纳入当前构建本示例与 gRPC 源码同仓故向上三级目录定位GRPC_FETCHCONTENT配置期用 CMake FetchContent 拉取指定 tag 的 gRPC默认分支通过find_package(Protobuf CONFIG REQUIRED)与find_package(gRPC CONFIG REQUIRED)使用系统已安装的 gRPC。构建时按需设置对应宏例如使用系统安装的 gRPC 时无需额外参数直接执行cmakemake即可。2.3 启动与运行构建完成后先在第一个终端启动服务端默认监听50051端口$ ./server服务端启动时会打印Server listening on 0.0.0.0:50051。然后在另一个终端运行客户端$ ./client两个二进制均支持 absl 风格的命令行参数客户端可用--targetlocalhost:50051指定服务端地址默认值即localhost:50051见 client.cc服务端可用--port50051指定监听端口见 server.cc。三、客户端源码解析set_deadline 与 4 个测试用例客户端核心逻辑在 client.cc 的unaryCall函数中其调用链为用Greeter::NewStub(channel)基于 channel 创建服务桩构造HelloRequest请求与ClientContext设置 1 秒 deadlinecontext.set_deadline(std::chrono::system_clock::now() std::chrono::seconds(1));通过异步 APIstub-async()-SayHello(...)发起调用并用std::mutexstd::condition_variable手工同步等待完成回调打印[label] wanted 期望状态码, got 实际状态码。main函数依次发起 4 个调用client.ccgrpc::StatusCode中OK 0、DEADLINE_EXCEEDED 4序号label请求 name期望状态码验证目标1Successful requestworldOK(0)正常调用在 deadline 内完成2Exceeds deadlinedelayDEADLINE_EXCEEDED(4)服务端故意延迟触发超时3Successful request with propagated deadline[propagate me]worldOK(0)单级传播后仍在 deadline 内4Exceeds propagated deadline[propagate me][propagate me]worldDEADLINE_EXCEEDED(4)两级传播后超时注意 include 部分的#ifdef BAZEL_BUILD分支client.ccBazel 构建时从examples/protos/helloworld.grpc.pb.h引入否则按helloworld.grpc.pb.h引入这正是BUILD文件中BAZEL_BUILD宏的用途。四、服务端源码解析CallbackService 与两类测试场景服务端实现于 server.cc采用Callback APIGreeterServiceImpl继承Greeter::CallbackService重写SayHello返回ServerUnaryReactor*。服务端在构造函数中创建指向自身的self_channel_grpc::CreateChannel(self_address, ...)供转发调用复用。SayHello的处理逻辑server.cc按优先级分为三支4.1 场景 Adeadline 传播[propagate me]前缀if (absl::StartsWith(request-name(), [propagate me])) { // 先睡 800ms模拟一层处理耗时 std::this_thread::sleep_for(std::chrono::milliseconds(800)); // 用当前服务端上下文派生新客户端上下文deadline 随之传播 std::unique_ptrClientContext new_context ClientContext::FromCallbackServerContext(*context); // 去掉前缀后把请求转发回自身 new_request.set_name(request-name().substr(14)); stub-async()-SayHello(new_context.get(), new_request, reply, ...); }这是整个示例最核心的一行ClientContext::FromCallbackServerContext把服务端收到请求时携带的上下文含 deadline、metadata 等转换为发起新 RPC 所需的客户端上下文。也就是说转发调用继承的是原始客户端设置的 deadline而非重新计时。4.2 场景 B响应延迟delay请求if (request-name() delay) { std::this_thread::sleep_for(std::chrono::milliseconds(1500)); }服务端故意睡眠1.5 秒远超客户端 1 秒的 deadline从而稳定触发DEADLINE_EXCEEDED。4.3 场景 C正常回复其余请求直接reply-set_message(request-name())并以Status::OK结束。服务端启动流程在RunServer中完成ServerBuilder通过AddListeningPort(server_address, grpc::InsecureServerCredentials())监听0.0.0.0:port无鉴权RegisterService(service)注册服务BuildAndStart()启动后server-Wait()阻塞等待。五、深入理解deadline 传播的端到端语义结合第 3、4 个测试用例可以清楚地验证 deadline 的端到端语义用例 3[propagate me]world服务端第一层睡 800ms → 转发world给自己立即回复。总耗时约 800ms小于 1 秒成功。此时转发调用共享的是原始 deadline剩余时间约 200ms 仍充足。用例 4[propagate me][propagate me]world第一层睡 800ms → 转发[propagate me]world→ 第二层再睡 800ms → 转发world。两级累计约 1600ms超过原始 1 秒 deadline因此最终状态为DEADLINE_EXCEEDED。值得注意的是单看每一层 800ms 的睡眠都小于 1 秒但 deadline 不会在每层重置——它是从最外层客户端一路贯穿整个调用链的。这正是分布式系统中超时必须在调用链顶端统一设定、逐层传播这一最佳实践的具象化任何一环的延迟累计超过整体 deadline整个调用链都会快速失败而不会无限等待。六、预期输出与验证一切顺利时客户端应输出 README 中给出的四行结果与源码中expected_code完全对应[Successful request] wanted 0, got 0 [Exceeds deadline] wanted 4, got 4 [Successful request with propagated deadline] wanted 0, got 0 [Exceeds propagated deadline] wanted 4, got 4若修改context.set_deadline的秒数例如缩短为 500ms用例 3 也会变为超时若将服务端delay分支的睡眠时间缩短到 1 秒以内用例 2 则会成功——读者可以借此参数化地观察 deadline 边界行为验证本文对传播语义的分析。七、在本仓库中的定位本示例随 MongoDB 仓库 vendored 的 gRPC 源码树一起分发src/third_party/grpc/dist/examples/cpp/deadline/是 gRPC 官方示例集的一部分配套的还有同目录下的 compression、load_balancing、metadata 等 C 示例以及 protos 目录 中共享的 proto 定义。对于需要在 C 服务中控制 RPC 超时、构建跨服务调用链容错能力的开发者ClientContext::set_deadline与ClientContext::FromCallbackServerContext是两个可直接迁移到生产代码的关键 API本示例即为最精简的可运行参考。【免费下载链接】mongoThe MongoDB Database项目地址: https://gitcode.com/GitHub_Trending/mo/mongo创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表