brpc Server push 服务端推送远程事件与 Restful 回调两种实现方案详解【免费下载链接】brpcbrpc is an Industrial-grade RPC framework using C Language, which is often used in high performance system such as Search, Storage, Machine learning, Advertisement, Recommendation etc. brpc means better RPC.项目地址: https://gitcode.com/GitHub_Trending/brpc/brpc服务端推送Server push是 RPC 框架中一个相对特殊但高频的场景当 server 端发生某个事件后需要立刻主动向 client 发送消息而不是像普通 RPC 那样被动地等待 client 请求再回复。本文基于 brpc 官方文档 docs/cn/server_push.md 展开系统讲解 brpc 推荐的两类推送实现——**远程事件Remote event**与Restful 回调Restful callback并结合 controller.h、controller.cpp 等源码与测试用例深入剖析其底层机制与工程实践。读完本文你将掌握如何在 brpc 中设计注册式推送、理解push 即超长 RPC 的 response这一核心思想并能在生产系统中组合 RPC 与消息队列构建既及时又不漏通知的可靠推送链路。一、什么是 Server push先厘清方向与本质在常规 RPC 中调用方向永远是 client → serverclient 发请求server 处理并回送 response。而server push乍看方向相反似乎是 server 主动访问client显得特殊。但文档给出一个精辟的观察server 发回给 client 的 response其传输方向不也是与client 访问 server相反吗也就是说方向相反本身并不特殊特殊的是时序——client 可能在任意时刻收到来自 server 的消息。为了理解 response 与 push 的真正区别文档假设client 随时可能收到 server 推来的消息并推敲出两个细节client 首先得认识 server 发来的消息否则是鸡同鸭讲client 还得知道如何应对这些消息如果 client 上没有对应的处理代码消息依然无用。换句话说client 必须对 server 的消息有准备而这种准备往往还依赖 client 当时的运行上下文比如当前页面、当前用户会话、当前连接等。综合来看由 client 告知 server我准备好了注册之后 server 再通知 client是更普适的模式。这个模式中的push其实就是response——一个超时很长甚至无限长 RPC 的 response。这是理解 brpc 推送方案的第一性原理推送 预先注册的长 RPC 的迟到 response。1.1 无需注册的特例HTTP/2 Push Promise 与受限双向通信文档同时指出在非常明确的场景中注册步骤可以省略。典型代表是 HTTP/2 的 push promiseRFC 7540 §8.2浏览器client并不需要向 server 注册因为双方都心知肚明——任务就是让 client 尽快下载必要的资源。由于每个资源有唯一 URIserver 可以直接把资源连同 URI 推给 clientclient 看到后缓存起来避免下次重复访问。类似的一些协议提供的双向通信也不是通用的推送实现而是在限定场景中提升推送效率例如多媒体流、格式固定的 key/value 对等。在这些场景下client 默认能处理 server 可能推送的所有消息所以无需额外注册。但即便如此推送仍可被视为response——它是 client 与 server早已约定好的请求的 response。这个特例对设计者的启示是是否保留注册步骤取决于 client 能否零成本地、无条件地理解 server 的所有推送内容。只要 client 需要依赖自身上下文才能处理消息注册式方案就不可避免。二、方案一远程事件Remote event——注册 通知的两段式异步 RPC2.1 工作流程远程事件模式与本地事件类似分为两步注册registration和通知notification。注册client 发送一个代表事件注册的异步 RPC至 server处理事件的代码写在对应的 RPC 回调done中等待这个 RPC 同时也在等待通知——server 收到请求后不直接回复通知等到对应的本地事件在 server 端触发时server 才调用done-Run()通知 client事件发生了。由此可见server 端同样也是异步的处理线程在收到注册请求后立即返回真正的工作由后续的事件源另一个线程、定时器、其他业务逻辑触发最终通过调用 done 来结束这条悬置的 RPC。2.2 连接断开与资源回收NotifyOnCancel 的正确用法这个过程中如果连接断开client 端的 RPC 一般会很快失败client 可以选择重试或结束。而 server 端必须通过Controller.NotifyOnCancel()及时获知连接断开的消息并删除无用的 done——否则服务端会为那些永远等不到响应方的请求泄漏回调对象。从源码看controller.h 对这套机制给出了精确定义IsCanceled()返回 client 是否取消了 RPC 或连接是否已断开server 可以据此放弃回复注意RPC 到达 deadline 并不会影响该函数即使超时它仍可能返回 falseNotifyOnCancel(callback)请求被取消或连接断开时调用给定回调。该回调保证恰好被调用一次若 RPC 正常完成未被取消/连接未断开回调会在完成后被调用若注册时 RPC 已经被取消或连接已断开回调会被立即调用。且每个请求最多只能调用一次NotifyOnCancel()。其底层实现位于 controller.cppNotifyOnCancel()通过Socket::Address获取当前对端连接若连接已断开则直接返回回调由ClosureGuard兜底释放否则创建bthread_id并调用sock-NotifyOnFailed(_oncancel_id)注册到 socket 上当连接失败时由RunOnCancel回调触发。源码注释还特别说明controller.cpp该回调经由Socket::SetFailed触发属于低频路径为避免阻塞 SetFailed回调会在新的 bthread 线程中执行。测试用例 test/brpc_controller_unittest.cpp 验证了两种触发场景notify_on_failed注册NotifyOnCancel后调用brpc::Socket::SetFailed(id)触发连接失败回调被异步执行IsCanceled()返回 truenotify_on_destruction注册回调后直接deleteController回调同样被触发保证销毁时释放资源。这两个测试印证了文档强调的两点回调一定会被调用不会泄漏 done且只会被调用一次。2.3 与 Long Polling 的关系这个模式在原理上类似long polling长轮询。文档评价它听上去挺古老但可能仍是最有效的推送方式。其工程含义是与其维护一条长连接并自行实现推送协议不如直接复用 RPC 的连接管理、超时、重试、负载均衡等成熟能力让推送天然获得 RPC 的可靠性语义——这正是 brpc 推荐该方案的根本原因。2.4 幂等性RPC 层代劳远程事件模式的幂等性由 RPC 框架代劳——done 只被调用一次。无论连接是否抖动、server 是否重试brpc 都会确保回调不重复执行client 无需自己处理重复通知。这与其他推送方案如下一节的 Restful 回调形成鲜明对比后者必须由业务代码自行保证幂等。2.5 一个直观的代码锚点cancel_c 示例brpc 的 example/cancel_c 示例虽然不是完整的推送实现但完整展示了本文所需的两块积木server 端server.cpp服务方法通过brpc::ClosureGuard done_guard(done)以 RAII 方式管理 done。注释明确指出如果需要异步处理请求就done_guard.release()把 done 的所有权转移出去——这正是远程事件模式中不立即回复等到本地事件触发时再done-Run()的写法基础client 端client.cpp通过cntl.call_id()获取CallId配合brpc::StartCancel可以取消另一个进行中的异步 RPC。结合 client.md 对异步 RPC 的说明我们可以勾勒出远程事件 client 的骨架发起异步注册 RPC → 在 done 中处理推送事件 → 连接断开导致 RPC 失败时根据业务需求选择重试或结束。三、方案二Restful 回调Restful callback——注册 URL事件到达时回调3.1 工作流程与核心区别client 希望在事件发生时server 调用一个给定的 URL并附上必要的参数。该模式与远程事件模式的关键区别在于server 在收到 client 注册请求时可以直接回复因为事件的触发不由注册用 RPC 的结束引起。换句话说注册 RPC 是即收即回的client 收到确认后就可以去干别的事后续事件由 server 端独立地通过 HTTP 请求回调 client 的 URL。由于回调只是一个 URL它可以存放于数据库或经消息队列流转因此灵活性很高在业务系统中使用广泛——比如异步任务完成通知、审批流状态变更、订单状态回调等场景。3.2 注册与回调的对应关系唯一标识符的设计URL 和参数中必须有足够的信息使回调能够知道这次调用对应哪一次注册。因为client 未必一次只关心一个事件可能同时注册了多个事件即使同一个事件也可能由于网络抖动、机器重启等因素被注册多次。文档给出两种等价的做法本质都是把唯一标识符放在不同位置固定路径 唯一 ID回调 URL 是固定路径client 在注册请求中置入一个唯一 ID如 UUIDserver 在回调时把该 ID 原样带回唯一路径client 为每次注册生成唯一路径如https://client.example/cb/{uuid}URL 本身即可区分。本质上两种形式是一样的只是唯一标志符出现的位置不同参数 vs 路径。无论采用哪种回调端拿到 ID 后都要能据此定位到本次通知属于哪次注册、该执行什么业务逻辑。3.3 幂等性必须由回调业务自行保证回调应处理幂等问题Idempotence。原因在于server 为了确保不漏通知在网络出现问题时往往会多次重试发送回调如果第一次的通知已经成功生效后续的重复通知就不应该再产生效果例如不应重复扣款、重复发消息、重复写库。对比两种方案的幂等保障维度远程事件Remote eventRestful 回调Restful callback幂等保障方RPC 框架done 只被调用一次回调业务代码自己保证通知通道原注册 RPC 的 response独立的 HTTP 回调请求注册时机注册 RPC 悬置等待注册 RPC 立即返回灵活性依赖 RPC 连接回调是 URL可入库、可走消息队列3.4 组合 RPC 与消息队列兼顾及时性与可靠性为了避免重要的通知被漏掉文档建议用户灵活组合RPC 和消息队列各取所长RPC 的时效性和开销都明显好于消息队列适合作为第一优先级通知通道但由于内存有限server 在重试过若干次数后仍然失败的话就必须把这部分内存空出来去做其他事情不能无限期地占用内存重试此时应把通知投递到消息队列中利用其持久化能力做较长时间的重试直至成功辅以回调的幂等性就能使绝大部分通知既及时、又不会被漏掉。这构成一个典型的两级投递可靠性模型先走 RPC 快通道低延迟、多次快速重试失败降级到消息队列慢通道持久化、长时重试而回调端的幂等逻辑保证了两个通道可能造成的重复投递不会产生副作用。这也是生产系统中推送类业务支付通知、订单回调等最常用的工程组合。四、方案选型何时用远程事件何时用 Restful 回调综合文档论述可以给出如下选型思路偏好事件与 RPC 生命周期绑定、client 需要最小化重复处理、且 client 是可编程的 RPC 客户端→ 选远程事件。它把推送伪装成一个很长的 RPC自动获得连接管理、超时、幂等done 仅一次等 RPC 语义实现成本低、语义清晰偏好高灵活性、回调可跨系统流转入库、走消息队列、或 client 是浏览器/第三方 HTTP 服务→ 选Restful 回调。URL 是天然的中立载体与具体 RPC 框架解耦代价是必须自己实现注册标识与幂等。两种方案的共同原则是文档反复强调的push 的本质是 responseclient 必须先注册/有准备server 的通知才有意义。选择方案时核心权衡点是 client 能否处理重复通知远程事件免费获得Restful 回调需自付以及通知通道是否需要跨系统持久化流转Restful 回调天然支持远程事件依赖连接存续。五、总结brpc 官方文档 docs/cn/server_push.md 给出的两条推送路径本质上是对server 主动通知 client这一需求的两次化简远程事件把推送化简为一条超长/无限超时的异步 RPC 的 response注册与通知复用 RPC 通道由 controller.h 的NotifyOnCancel()/IsCanceled()保障连接断开时的资源回收由框架保证 done 恰好执行一次幂等由 RPC 代劳Restful 回调把推送化简为注册一个 URL事件到达时由 server 发起 HTTP 回调注册与回调的对应关系通过唯一 ID参数位或路径位确立幂等由回调业务自证并可与消息队列组合形成及时 可靠的两级投递。两者的共同内核是push 不是 server 反过来访问 client而是 client 预先注册的请求在事件发生时获得了 response。理解这一点就掌握了在 brpc 中实现任何推送类业务实时通知、事件订阅、异步任务回调的通用方法论。若要进一步理解 client 异步 RPC 的完整能力可继续阅读 docs/cn/client.md若想了解 server 端服务的编写与启动方式可参考 docs/cn/server.md。【免费下载链接】brpcbrpc is an Industrial-grade RPC framework using C Language, which is often used in high performance system such as Search, Storage, Machine learning, Advertisement, Recommendation etc. brpc means better RPC.项目地址: https://gitcode.com/GitHub_Trending/brpc/brpc创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考