第13章IM服务——13.7 消息回执

13.7 消息回执

很多产品的IM服务都支持消息回执功能,即消息发送者在单聊中可以看到对方是否已读取某条消息,以及在群聊中可以看到一条消息已被哪些成员读取。

13.7.1 上报已读消息

要想知道一条消息是否已被读取,自然需要依赖消息接收者把已读事件上报给服务端,其重点是上报时机。

当一条消息被展示给用户时,如果客户端选择立刻上报已读事件,则可能会给服务端带来访问压力,尤其是群聊。假设一个大群有300人活跃在线,群内每下发1条消息,就会导致300个客户端的已读上报事件反向访问服务端。

实际上,对于一条消息是否已被读取并不需要很强的实时性,客户端没有必要在已读事件发生后就立刻上报给服务端,更好的做法是客户端周期性地(如每隔3s)汇总每个会话最后一条已读消息的Seq上报。从消息发送者的视角来说,也不需要实时知道对方是否已读取消息,所以发送者的客户端也可以周期性地从服务端查询消息是否已被读取。

接下来,我们讨论如何存储消息已读事件。

13.7.2 记录已读消息

记录已读消息最直接的方式是为每条消息和每个已读用户保存关联关系,但是这种方式严重占用存储空间,并无实用性可言。实际上,我们可以利用消息有序性的特点,只记录每个用户在会话中最后一条已读消息的Seq last_read_seq,判断一条消息是否已被读取只需要看消息的Seq值是否小于或等于last_read_seq值即可。如果消息的Seq值更大,则说明此消息未被读取,否则说明此消息已被读取。

这种记录最后一条已读消息Seq的方式既适合单聊会话,也适合群聊会话,只不过对于群聊会话来说,有一个小细节需要注意:某时刻被拉入群聊的新成员,并不统计对于之前的群消息此成员是否已读,只能默认新成员对其进群之前的全部群消息是已读的。

我们在用户会话链中增加last_read_seq字段,消息发送者周期性地检查自己发布的消息是否可以被更新为已读状态:

  • 在单聊会话中尚未被对方读取的消息;
  • 在群聊会话中尚未被全部成员读取的消息。

消息接收者在读取某条消息后,客户端将已读事件上报给服务端,服务端将更新用户会话链中对应的last_read_seq值。如图13-9所示,用户A在4人群聊会话X中发送了一条Seq值为100的消息,用户B和用户D在读取了这条消息后,分别上报更新last_read_seq字段值为100;用户A拉取此消息的已读状态,发现有两个用户的last_read_seq值大于或等于100,说明这两个用户已读。

image-20250503225019756