消息发送模块
按照笔者的想法,一个消息发送模块,要思考以下几个问题:
1.消息如何存储?
2.如何设计离线消息?
3.怎么保证用户及时收到消息?
4.消息如何保证不丢失?
5.如何解决大文件类型消息占用带宽问题?
6.请求消息接口是通过websocket还是http?
首先,要先清楚以下几个流程:
1.用户上线
2.用户发送消息
一个用户要给另一个用户发送消息,流程大概如下,
3.接收方接收消息的具体流程
第一种:用户不在线,刚上线收到消息
第二种:用户在线,收到新消息
4.性能分析
yokna目前只会用mysql,所以数据都存在mysql中,导致整个系统的性能瓶颈一主要集中在数据持久化上,如果换成非关系型数据库有可能会快上些许,用netty的话,32G centos可以支持不低于5w用户的连接(只测试到了5w用户),不做持久化的话,消息传输延迟在1.5s以内(同时给5w用户发送一条系统消息)。
瓶颈二在于群发消息机制上,上图可以看出逻辑设计上仍有缺陷,比如,为了保证消息的可靠性,所有消息都必须要持久化,假如现在有一个群发消息有5w个用户,先做完持久化,将这条消息对应每一个用户存为未读,也就是5w条记录,然后再给在线的用户推送新消息,当用户读取了消息以后,就从离线消息库中迁移走这条数据。整个设计我现在看来头皮都是麻的,但是暂时没有想到更好的解决方案,希望有大佬能在评论区留言交流。
5.问题解答
1.消息如何存储?
目前采用的是存mysql数据库,表结构如下:
字段 | 含义 |
---|---|
id | 自增主键 自增长在数据迁移过程中比较苦难,在高并发场景下不推荐。 |
Creat_time | 消息创建时间 |
From_uid | 发送方的id 用户发送、单位发送业务可以满足,但是系统推送消息是不能满足这个业务需求的,换成数据字典,不定期活动–建立字典表,通过维护字典来维护活动 |
Msg_body | 消息体 长度限制,二进制文件,不支持检索,检索需要重新思考策略 |
Msg_type | 消息的类型,是系统推送还是部门之类 |
To_uid | 发送方的id |
Update_time | 消息发送成功的时间 |
Msg_level | 消息的紧要程度 |
Msg_need_phone | 消息是否需要在移动端短信推送 |
Msg_need_weixin | 消息是否需要微信推送 |
Msg_need_more | 消息是否需要在其他平台推送 |
Msg_success_sendtime | 消息成功发送的时间 |
还需要有:短信推送成功与否、短信推送时间、消息是否删除状态
2.如何设计离线消息?
离线消息就是需要将消息标注为未读状态,不再过多展开,主要聊一聊ws发送什么样的消息。
如上表所示,我们需要发送的data最少要有四个属性:
{
"type": 4,
"fromUserName": "systemCallName",
"toUserNames": [
"18180970000"
],
"msgText": "你们好,这是系统推送消息!"
}
type是自己定义的消息类型
msgText最好是一个富文本类型,避免以后出现图片、文件或者其他类型的消息。
3.怎么保证用户及时收到消息?
使用netty框架,管理在线用户,当有该用户的消息,就通过该用户对应的channalPipeLine将未读消息提示发过去。
4.消息如何保证不丢失?
消息先做持久化,保证持久化完成后,消息怎么样都不会丢失。因为消息持久化时,全部为未读状态,只有当用户主动去点击查看消息详情时,消息才会变为已读,已读消息不再推送给用户,而是通过查看历史消息来显示。
5.如何解决大文件类型消息占用带宽问题?
通过先推送未读消息条数,而不是直接推送消息内容这样的机制来实现。
6.请求消息接口是通过websocket还是http?
yokna个人认为,用户与用户之间发送的消息通过websocket来发送,最多是在消息解析上需要多消耗点性能,netty是基于事件驱动的,在管理请求发送过程中,调用linux内核中的epoll模型,动态发现socket套接字文件状态变化,也就是说,netty是有活来了我就干,没活的时候我就监听有活没有,在用户上线时是建立了一个websocket连接的,http会额外建立连接。但是http也不是完全没用,可以在微服务调用时提供接口,给其他服务提供消息代发的服务嘛,比如其他服务有定时任务,像各种分析结果、周数据统计之类的,需要推送给用户时,就可以通过http请求调用消息发送服务。
结语
这一节主要是在聊yokna如何设计的,并思考如何改进,下一节主要都是代码部分了,如何改进这消息发送板块,还请大家多多留言交流!