目录
PS:
最近在做一些消息相关的内容,对消息做了一下总结,包括发送、接收、存储等,记录一下,欢迎大家交流拓展。
存储消息
redis临时存储,数据结构zset
kafka异步持久化到db,由于消息表数据量比较大,msg表按照createdTime分区。
发件箱
sendBox redis zset实现。
zadd nx,key:固定前缀+senderId,score:msgId,member:clientId
发件箱的作用?
- 发件箱根据雪花算法生成统一的msgId。
- 主要作用是为了防止同一条消息重复发送。
- 客户端发送消息时,有clientId,防止弱网导致客户端显示发送失败,但实际上服务端成功处理的情况。
- 相同clientId的消息不会发到receiver的收件箱。
最大消息限制
最多保留100条消息,zremrangebyrank
收件箱
inBox:redis zset实现。
zadd,key:固定前缀+receiverId,score:msgId,member:msg
发送消息最终是添加到receiver的收件箱,这样拉取消息时,每个人拉取自己的收件箱即可。
最大消息限制
最多保留最近一个月的消息,zremrangbyscore
消息结构
消息类型
发消息设计的数据结构需要支持文本、语音、图片、视频等格式。
可以统一定义一个字段为msgType,然后区分不同类型,定义枚举,每一种类型对应一种结构。
如msgType为“text”,对应结构为:
“text": {
"value": "发送文本消息"
}
如msgType为”image“,对应结构为:
”image": {
"url": "https://xxxxxxxx",
"width": 80,
"height": 80
}
其他类型同理。
最终类似于这种:
type Message struct { Id int64 `json:"id"` ClientId string `json:"clientID"` MsgType string `json:"msgType"` Text *Text `json:"text,omitempty"` Image *Image `json:"image,omitempty"` Audio *Audio `json:"audio,omitempty"` Video *Video `json:"video,omitempty"` .... } |
拓展
发送消息肯定不止这几种类型,其它一些特殊样式的如卡片、系统提示信息、问卷、跳转等等。
上面的结构也是易于拓展的,只要定义不同的msgType及其对应的结构,前后端统一结构即可。
校验内容
敏感词服务校验,接口调用判定。
资金来往、索要联系方式等风险提示。
风险用户提示,审核服务,接口调用判定。
拉取消息
- 客户端根据since和limit来拉取消息,每次取本地最新消息的msgId作为since。
- 服务端从zset中拉取msgId大于since的消息(zrangeByScore),小于since的消息会从zset中删除。
- 大于since且在limit之间的消息不能删除,因为不能判定客户端正常收到这些消息。
- 每次删除since之前的消息,能保证删除的消息,一定是被客户端收到的(不然since不可能传过来)。
- 触发拉取消息时机:
- 收到普通push、静默push。厂商提供的push接口。
- 每间隔5s拉取消息