服务器推送

qianmoQqianmoQ· 更新于 2026-10-05· 阅读 5 分钟· 0 次阅读

登录后可跨设备保存划线和私人笔记登录

服务端推送

学习如何进行服务端推送。

服务端推送

"服务端推送"指的是:在事件发生后由服务端主动向客户端发送消息,而不是像普通 RPC 那样被动地应答客户端。以下两种方法推荐用于在 brpc 中实现服务端推送。

远程事件

与本地事件类似,远程事件分为两个步骤:注册和通知。客户端向服务端发送一个异步 RPC 进行注册,并把事件处理代码放在该 RPC 的回调中。这个 RPC 也是等待通知的一部分,即服务端收到请求后不会直接响应,而是直到本地事件触发才调用 done->Run()(通知客户端)。可以看到,服务端同样是异步的。如果在此过程中连接断开,客户端会很快失败,并可以选择重试其他服务端或结束该 RPC。服务端应通过 Controller.NotifyOnCancel() 感知到断连,并及时删除无用的 done。

这种模式在某种意义上类似于 long polling],听起来可能有些陈旧,但或许仍是最有效的方法。乍一看,"服务端推送"是服务端访问客户端,与普通的客户端访问服务端方向相反。但你是否注意到,服务端返回给客户端的响应,其方向同样是与"客户端访问服务端"相反的?为了理解响应与推送的区别,让我们在"客户端可能随时收到来自服务端的消息"这一假设前提下来分析这个过程。

  • 无论怎样,客户端都必须能理解来自服务端的消息,否则消息就没有意义了。
  • 客户端还需要知道如何处理这些消息。如果客户端没有相应的处理代码,消息同样是无用的。

换句话说,客户端应该对来自服务端的消息"做好准备",而这种"准备"往往依赖于客户端当前所处的编程上下文。综合各种因素,更通用的做法是:客户端先告知服务端"我准备好了"(注册),之后服务端再通过事件通知客户端。在这种情况下,"推送"本质上只是一个"响应",只不过它拥有一个非常长、甚至无限的超时时间。

在某些场景下,注册可以被省略,例如 HTTP/2 中的 push promise](https://tools.ietf.org/html/rfc7540#section-8.2),就无需 Web 浏览器(客户端)向服务器注册,因为客户端和服务器都清楚,它们之间要做的任务就是让客户端尽快下载必要的资源。由于每个资源都有唯一的 URI,服务器可以直接将资源和 URI 推送给客户端,由客户端缓存这些资源,从而避免重复访问。同样,具备"双向通信"能力的协议在特定场景(如多媒体流、固定格式的键值对等)下,很可能比实现通用推送更能提升效率。客户端知道如何处理服务器可能推送的消息,因此不需要额外的注册。推送仍然可以被视为一次"响应":响应客户端已经且始终向服务器承诺过的请求。

Restful 回调

客户端希望在事件发生时,携带必要的参数调用某个给定的 URL。在这种模式下,服务器收到注册请求后可以直接回复客户端,因为事件并不是由 RPC 的结束触发的。此外,由于回调只是一个 URL,可以存入数据库和消息队列,这种模式非常灵活,在业务系统中被广泛使用。

URL 和参数必须包含足够的信息,使回调能够知道哪条通知对应哪个请求,因为客户端可能同时关心多个事件,或者由于网络抖动、机器重启等原因,同一事件可能被注册多次。如果 URL 路径是固定的,客户端应在注册请求中放置一个唯一的 ID,服务器在响应中原样返回该 ID。或者客户端为每次注册生成唯一的路径,使每个请求天然可区分。这些方法本质上是相同的,只是唯一标识符的位置不同。

回调必须处理幂等性]。服务器在遇到网络问题后可能会重试并发送多次通知。如果第一次通知已经成功,后续通知不应再产生影响。"远程事件"模式在 RPC 层面保证了幂等性,确保 done 被且仅被调用一次。

为了避免漏掉重要的通知,用户往往需要灵活地结合 RPC 与消息队列。RPC 在延迟和效率上远优于消息队列,但由于内存有限,服务器在几次重试失败后需要停止发送 RPC,把内存用于更重要的事务。此时最好将通知任务转移到持久化消息队列中,消息队列能够在很长一段时间内持续重试。配合 URL 回调的幂等性,绝大多数通知都能被可靠、及时地发送。


最后修改于 2022 年 1 月 9 日:[基于 hugo 的新版 brpc 网站 (94b25d711)]](https://github.com/apache/brpc-website/commit/94b25d7110944f3d4b2071b5188c691b23ffe3a9)

评论

登录后参与评论

正在加载评论…