文章

简单公告

简单公告

基于客户端的公告实现

招新平台的公告设计确实简单,除了发布、查询、编辑功能以外,对用户的已读/未读状态维护得并不完整,用户的已读状态在客户端保存,通过轮询发送本地已读公告id请求服务端未读id,如果有新未读id,直接显示。

在赶进度/人手不足状态下是可理解的,因为从业务角度讲,公告的已读状态属于不重要内容,不用担心丢失或被篡改,因此可以选择放在客户端,但问题是用户更换设备登录/更换浏览器登录就需要重新读一遍公告,体验不好,这里先分析一下招新平台现在的实现方式。

根据分析,只需要维护:公告的增删改查、根据已读id查询未读id这两大块功能。

增删改查设计较为简单,不再赘述,唯一需要注意的地方是接口鉴权,一定要验证管理员身份再支持公告的修改,否则会出大问题,具体参考RBAC权限管理。

对于根据已读id查询未读id这个功能,需要注意不必查询全部id后再在业务层做排除,可以直接使用数据库提供的不等于功能快速排除。

正确公告系统的设计

重点在已读/未读状态的维护,个人认为可以分为两种,推和拉。推模式为每个用户维护一个公告已读情况,拉模式为每个公告维护一个已读用户情况,应该根据用户数量和公告数量合理选择。

推模式

Redis bitmap 存储 未读/已读

首先,其实未读已读就是一个0/1的标记而已,可以维护一个Bitmap来实现,0未读,1已读。但是如果公告id使用雪花id来实现的话,会出现id过长的问题,因此需要配套为每个公告设置一个自增id。

然后,每当用户阅读一个公告,就更新bitmap对应位为1,这样可以方便的识别公告的已读未读情况。

放不下了怎么办?位图搞大点吧。。。。例如搞一个25565位的位图怎么都够了吧。

拉模式

Redis bitmap 存储 未读/已读

  • Redis存储 key为公告id bitmap存已读的用户id

所有公告分页查询时,先得到Mysql分页第一页的公告id,然后遍历查询Redis已读未读状态

未读公告的分页,需要查询所有的公告id(组织注册时间后的公告), 遍历请求redis得到 未读的公告id, 再进入Mysql 使用 in 进行分页

  • 优点: 新增公告无需维护关联关系

  • 缺点:

    • 分页查看所有未读的需求会随着公告的增多循环次数会增多
    • 全部已读需要遍历所有公告
    • 随着用户数增多单个key占用的内存持续增长

可以通过存储用户最后一次所有公告已读的时间戳,减少循环次数

扩展: Redis roaring bitmap 降低基数较大时bitmap的存储

License:  CC BY 4.0