这是我参与「第五届青训营」笔记创作活动的第7天。
今天主要是应用RabbitMQ优化了大项目的聊天系统,解决了大项目聊天系统的诸多问题。
项目初期聊天业务接口的编写思路及存在的问题
在项目初期,在GET项目的/douyin/message/chat/此PATH时,采用的直接去数据库查询相应的聊天列表的方案。
func GetMessageList(toUserId int, fromUserId int) (respMessageList []model.RespMessage, err error) {
var messageList []model.RespMessage
//去数据库查询出符合要求的Message信息
messageSendIdList := model.GetMessageIdList(toUserId, fromUserId)
messageReceiveIdList := model.GetMessageIdList(fromUserId, toUserId)
messageIdList := append(messageSendIdList, messageReceiveIdList...)
messageIdList = sort.QuickSort(messageIdList)
messageList = model.GetMessageList(messageIdList)
for _, message := range messageList {
respMessage := model.RespMessage{}
copier.Copy(&respMessage, &message)
respMessageList = append(respMessageList, respMessage)
}
for i, message := range respMessageList {
t := time.Time{}
fmt.Println(message.CreateTime)
t, _ = time.ParseInLocation("2006-01-02T15:04:05Z07:00", message.CreateTime, time.Local)
fmt.Println(t)
respMessageList[i].CreateTime = strconv.Itoa(int(t.Unix()))
}
return
}
但是,大项目的前端聊天界面,每过一秒都会重新发送一条GET请求(具体路径为:/douyin/message/chat/)给我们的后端服务器,这就会导致我们频繁地从数据库中拉取相同的MessageList,造成消息重复问题。
图1 前端频繁发送相同的get请求给后端
图2 造成消息重复问题
聊天记录重复加载的解决思路
针对已经编写好的前端的现有逻辑(每过一秒都会重新发送一条GET请求),我们找到了问题的根源所在。因为数据库的数据一直存在,所以每次GET请求调用Dao层的函数将数据库中的数据拉取出来,造成数据的重复问题。
是否可以只在第一次拉取的时候有数据,后续拉取空数据?用MySQL显然不容易实现,但是如果使用RabbitMQ在逻辑上则简单许多。
具体实现逻辑如下:
- 首先实现RabbitMQ的Queue中聊天记录的初始化--将数据库的消息写入到MQ的Queue中去。我们将数据库的Message信息拉取出来,得到一个MessageList,将这个MessageList通过josn.Mashal()序列化,转成一个byte数组。然后将此byte数组转成String类型,以str类型Json格式的形式存到我们的RabbitMQ的Queue中去。
- RabbitMQ的Queue中聊天记录初始化的时候,我们为了确保只初始化一次,将Queue的MAX-Length设置为1,并采用drop-head的过期策略,这样使得我们在初始化的时候总能初始化最新的数据库中的聊天记录到Queue中去,且让RabbitMQ的Queue中的消息始终只有一条,在消费的时候,前端也就不会二次加载我们的聊天记录,也就从本质上解决了我们的消息重复问题。
- 在接收到路径为/douyin/message/chat/的GET请求时,我们对RabbitMQ的Queue中的消息进行消费,消费后得到一个string类型的Json串。然后使用json.Unmashal()进行反序列化,重新得到消息列表,返回给前端。
发送新消息的实现思路
在发送新消息的时候,我们不仅仅调用Dao层的方法将Message信息写入到数据库中,还要将Message信息写到RabbitMQ的消息队列中去,和上面的处理思路十分类似,我们针对CurrentMessage信息Declare的Queue的MAX-Length仍然为1,并采用drop-head的过期策略。在接收到路径为/douyin/message/chat/的GET请求时,我们对RabbitMQ的Queue中的消息进行消费,然后反序列化,存到一个MessageList中去,最后将该MessageList返回给前端即可。
总结
今天找出了大项目聊天系统存在的问题,并找到了解决该问题的思路,明天我将会在大项目的用户聊天模块引入RabbitMQ,并分享解决该问题的具体代码实现。