跳转至

消息转发分类和投递策略

设计思路

通讯应用中非常重要的特性就是消息的实时性,用户在线时能不能快速地收到其他人的消息,能不能及时收到通知。还有离线消息补发:用户离线时错过了哪些最新消息?重连时能不能快速获取到这些消息;以及消息存储的可靠性,出于意外或者设备的变更,以往的消息能不能完整地保存不丢失? 这里涉及到的有 消息实时通知,热点消息缓存,消息的持久化,这三者在时间跨度上逐步增长,实时通知如果离线就失效,热点消息缓存在用户离线很久之后也会丢失,消息的持久化则是基本上永久地保存了消息 我们可以根据消息的重要性选择不同的策略:时效性很短的消息选择在线则实时通知,离线则丢弃。时效性适中的选择在线则实时通知,离线则加入缓存队列,重要的消息则是一定要持久化到数据库,在线的话就通知

同时发送消息的用户需要得到响应:消息发送是否成功,没有成功的话错误或者异常的内容是什么

所以需要有一个统一的信息封装,要有转发策略,要兼容不同的信息内容,以及如果投入离线队列的话,对应的离线队列是哪一个。信息封装后经过connection service统一处理转发,得到响应后通知用户

准备工作:创建辅助工具

目前消息分类:通知,用户聊天消息

消息转发策略:1.仅在线投递,离线丢弃;2.优先在线投递,不在线则存储;3.必须送达

目前1和3效果一样,3留作后面做更严格的扩展

消息投递结果:1.送达 2.投入离线队列 3.丢弃

定义枚举

enum class DeliveryPolicy
{
    OnlineOnly,
    PreferOnline,
    MustDeliver
};

enum class OfflineChannel
{
    Message,
    Notice
};

enum class DeliveryState
{
    Sent,
    Queued,
    Dropped
};

定义一个包装消息的类 envelope

struct OutBoundMessage
{
    DeliveryPolicy policy;
    OfflineChannel channel;
    OutboundPayload payload;
}

其中

using OutboundPayload = std::variant<ChatMessage, Notice, Json::Value>;

消息类型可以是聊天消息,通知消息,以及序列化的json,为了后面方便做扩展,我们用std::variant封装信息的payload,后面有新的类型的数据,只要再写一个DTO类加入就行。目前这个序列化的json主要是传递一些调试信息和报错

消息转发的核心逻辑

对于目前的几种消息,我们采用优先在线通知,离线放入缓存,数据库持久化兜底的机制,保证消息的实时性和可靠性。

实时消息通过用户的连接connection来进行发送,所以消息转发的逻辑主要在connection service中。Connection Service会根据消息的转发策略进行不同的处理,所以对应的,转发的结果也有多种:

我们定义一个转发结果类 DeliverResult,以便把转发的失败和转发异常区分开

struct DeliveryResult
{
    DeliveryState state{DeliveryState::Dropped};
    std::string reason;
}

核心转发接口是这样的:

DeliveryResult DeliverMessage(const std::string& recipient,
    const OutboundMessage& msg);

这里分发的逻辑是,先看msg的policy:

OnlineOnly:用户在线时实时推送。如果连接池中没有对应用户连接,说明用户不在线,不转发,不入缓存队列,不持久化。设置返回结果的状态是dropped

PreferOnline和MustDeliver:有连接时实时发送,同时存储到数据库中;没连接时要加入缓存中并持久化到数据库

后面可以延伸更多分发逻辑,但基本就是这三种操作的排列组合:是否实时转发?是否入缓存队列?是否持久化?