教你六种方式实现聊天室

如果技术栈不一致的同学请尽量看懂架构和概念,自己用自己的技术栈去架构自己的聊天室!

此项目为前后端分离项目,基于vue+springboot构建

欢迎star

前端:以 vue 为核心的 vue全家桶(vue+vuex-状态管理+vue-router-路由管理+axios-HTTP库),UI框架使用了iview+VueSax,打包和初始化项目工具使用 webpack 来代替vue-cli.图片上传使用了他人的 图床,发送框使用了 quill 富文本编辑器。

后端:使用了springboot 框架,数据持久层框架使用了 mybatis,数据库使用mysql.

中间件:缓存中间件-redis,消息队列-rabbitmq。

加密使用 AES 加密

通讯协议使用 WebSocket和HTTP协议

项目演示:

1.登录注册部分

由于登录注册太过于简单,所以这里直接给出账号密码名单使用
Mr3mCT.png
Mr3Rxg.png
当此账号已经登录时,isOnline 开关会显示打开状态,点击登录会出现提示

切换另一个账号即可,若无可用账号,请等待 orz

2.聊天

注:这个项目里只有群聊,没有设置单人聊天,因单人聊天无非是群聊的一个特例,这里不予实现。

主界面
Mr81SS.png

项目里给出了6个聊天室,分别对应不同实现方式。

–聊天室一:
MrGFkq.png
仅使用WebSocket实现,仅能在线聊天,离线用户收不到消息且无法读取未读消息

如图,前端通过http协议向后端发送数据,后端接收到数据后通过WebSocket协议向在线的人员发送数据。

–聊天室二:

使用`WebSocket+mysql`,提供了在线聊天,不在线用户上线后可拉取未读消息。

MrJKxS.png
如图,后端接收到聊天数据并给在线人员发送消息后再把数据按照一定的逻辑存在数据库里,当用户上线后,拉取未读消息即可,此时未读消息能且仅能拉取一次,拉取完毕后,消息就成为已读消息,并不能重读拉取。

–聊天室三:

缓存写扩散:使用WebSocket+redis+mysql。

MrUSdH.png

如图,每个用户维护一个 inBox,每条信息发给服务器时,给在线人员发生信息,并判断该条消息所属群组,并找到该群组下所有不在线人员,并把该消息写入不在线人员下的inBox下,当用户上线拉取消息时,只需从自己的 inBox 下拉取消息即可,该inBox是用redis的list实现的,是有序的(插入顺序),所以不存在消息时间混乱问题,当用户读取消息后,立即删除该inBox,保证了未读消息的唯一性.其中mysql用于消息的备份。

–聊天室四:

缓存读扩散:使用WebSocket+redis+mysql。

MrwCFJ.png
如图,每个用户需要维护一个outBox和一个已读消息索引,用户每发送一条信息,都存在自己的outBox里面,并存入order顺序表,当离线用户上线拉取未读消息时,首先读取自己已读消息索引和order顺序表,根据顺序表大小和已读消息索引之差找到自己所有未读消息,然后根据顺序表里的信息去找每一条信息发送者的outBox,并拉取每一条未读消息,拉取完毕后更新自己的已读消息索引即可。

–聊天室五:

缓存读扩散改良版:使用WebSocket+redis+mysql。

Mrc8Zq.png

在上一个聊天室中,由于每个用户有一个outBox,信息存储将是无序的,所以需要额外耗费空间来存储顺序,所以在改良版中,根据是群组的信息发送,所以只需维护一个群组的outBox,不再有个人的outBox,所以用户拉取历史消息时,只需比对自己消息索引和群outBox索引即可

– 聊天室六

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Ud9fR9yy-1574337351454)(https://s2.ax1x.com/2019/11/17/MrgYnA.png)]

消息队列:使用 WebSocket+Rabbitmq+mysql

这里使用rabbitmq作为消息中间件,每个群组是一个fanout 类型交换器,这个交换器的特点在于,每接收一条消息,交换器都会给绑定的队列发送这条消息,所以每个群组绑定一个fanout类型交换器,每个交换器下都绑定这每个用户的队列,用户每发送一条消息,每个队列都会收到这条消息,在线的用户把消息读取出去,不在线的用户上线拉取消息,这样的方式减少了手动判断用户和减少关注消息的顺序问题,统一交给消息队列处理。

三、其他

  • UI优化
    在这里插入图片描述
    登录过期提醒
    在这里插入图片描述
    新消息提醒
    在这里插入图片描述
    发送图片
  • 消息存储
    前端所有消息均存在 localstorge中,当前后登录账户不一致时,前一个账户消息将会被清空,如果前后登录账户一致,消息不会清空
    用户登录有效期在 30 分钟内,超过30分钟要重新登录

四、jvm 优化

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DgUeWo4c-1574337383543)(https://s2.ax1x.com/2019/11/17/Mr5lVJ.png)]

由图,根据jvm GC 分析,可以看出此应用最多的是 “短命的对象”,因为这个应用是一个 web应用,每一个请求完成后,此请求中所有创建的对象都是游离的 无Root的对象,也就是马上要被回收的对象,此时这个对象将在 Eden区存活,当Eden区满,此时会触发 minor Gc,此时所有已完成的请求中的对象均会在此销毁,当用户请求过多,即大量请求在几秒内出现时,此时大量对象将在第一次 minor GC 时存活,移到 Survivor 1区,但是如图所示,Old老年代区占用率一直不变,说明大部分对象在经历>=15次 minor GC 之前就被销毁,所以我们要做的优化其实没有很多,反过来看这个项目的构成,存活时间最长的应该是 WebSocket ,当用户信息量过大时,还是有可能让 WebSocket 相关对象度过>=15次的 minor GC ,但是由于项目的特性,几乎不可能发生 Full GC,也就是说当WebSocket对象在老年代中死亡时,这将很难被清除,因为无法进行Full GC,所以为了避免这种情况,有两种解决办法均可解决
1.当WebScoket断开连接时,主动置为NULL,重写 finalize方法,立即进行GC,而不用等待内存区满再进行。
2.修改jvm参数 :-Xms500m,设置了jvm启动内存(当时想着越大越好),-Xms500m(运行中最大内存),然而,本机运行卡死,服务器仅仅有1g的内存,必然带不动,所以恢复默认值

欢迎大家留言讨论
想要源码的留言,我将私信给出github

发布了29 篇原创文章 · 获赞 33 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/qq_42568510/article/details/103189681
今日推荐