系统消息模块的设计原理

一、消息的持久化存储

        之前我们做司机抢单功能的时候,用到了 RabbitMQ 消息队列。但是抢单消息被司机接受之后,消息就被队列给删除了,并不会持久化存储。毕竟抢单信息都是临时性的消息,并不需要持久化存储。但是其他的一些系统通知,无论司机或者乘客是否收到该消息,都要持久化存储。比如说钱包充值成功的通知,这样的消息就应该持久存储,除非司机手动删除消息。

        持久化存储系统消息,数据量很大,而且又不是高价值的数据。好像用 MongoDB 和 HBase 都可以,到底应该选用哪一个呢?如果数据的检索需要用上复杂的查询条件和表达式,那么用 HBase 更加的适合。毕竟 SQL 语句写起来很容易。反之,就可以用 MongoDB。恰好系统消息无非就是分页查询,没什么复杂的条件,所以我们用 MongoDB 就可以了。

二、创建数据表

        MongoDB 中没有数据表的概念,而是采用集合 (Collection) 存储数据,每条数据就是一个文档 (Document),文档结构很好理解,其实就是我们常用的 JSON,一个 JSON 就是一条记录。

1. 创建 message 集合

        集合相当于 MySQL 中的数据表,但是没有固定的表结构。集合有什么字段,取决于保存在其中的数据。下面这张表格是 Message 集合中 JSON 数据的结构要求。

注释:消息要发送到 RabbitMQ 上,所以需要 交换机exchange 字段。

在 com.example.hxds.snm.db.pojo 包中,创建 MessageEntity.java 类。

@Data
@Document(collection = "message") #mongoDB相关注解
public class MessageEntity implements Serializable {
    @Id
    private String _id;
    
    @Indexed #索引注解
    private Long senderId;

    private String senderIdentity;

    private String senderPhoto = "System.jpg文件的公网URL地址";

    private String senderName;

    private String msg;

    @Indexed
    private Date sendTime;
}

2. 创建 message_ref 集合

        虽然 message 集合记录的是消息,里面有接收者 ID ,但如果是群发消息 ,那么接收者 ID 是空值。这时候就需要用上 message_ref 集合来记录接受人和已读状态。

        比如说小程序每隔5分钟轮询是否有新的消息,如果积压的消息太多,Java系统没有接收完消息,这时候新的轮询到来,就会产生两个消费者共同接收同一个消息的情况,这会造成数据库中添加了重复的记录,如果每条MQ消息都有唯一的UUID值,第一个消费者把消息保存到数据库,那么第二个消费者就无法再把这条消息保存到数据库,解决了消息的重复消费问题。

        在 com.example.hxds.snm.db.pojo 包中,创建 MessageRefEntity.java 类。

@Data
@Document(Collection = "message_ref")
public class MessageRefEntity implements Serializable {
    @Id
    private String _id;

    @Indexed(unique = true) #唯一性约束、很关键
    private String messageId

    @Indexed
    private Long receiverId

    private String receiverIdentity

    @Indexed
    private Boolean readFlag

    @Indexed
    private Boolean lastFlag
}

 三、消息收发流程

        很多时候系统消息是群发的,也就是存在大量的接受人。假设网站有100万注册用户,我们要发送广播消息,理论上向 message 集合插入1条记录,然后向 message_ref集合添加100万条记录。如果有80万用户常年不登录,我们在 message_ref 表中一直保存他们的通知信息,太浪费存储空间了,最好能给消息记录设置个过期时间,自动销毁。很遗憾MongoDB没有过期时间这个机制,于是我们想到了RabbitMQ,消息队列里面的消息是有过期时间的。当系统要发送消息了,先把消息记录保存到message集合里面,然后向收件人的队列中发送消息。假设过期时间是1个月。这期间如果用户没有登录系统查收消息,那么队列中的消息就自动销毁了。

        如果用户在消息过期之前登录了系统,Java系统会从RabbitMQ的队列中接收消息,然后把消息保存到message_ref集合中,用消息队列,我们就可以为活跃用户持久化存储消息通知了。对于不活跃的用户,为了节省存储空间,我们不会为他们存储消息通知。 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

chengbo_eva

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值