太顶了,使用 Netty 实现了一个 IM 即时通讯系统

点击上方“芋道源码”,选择“设为星标

管她前浪,还是后浪?

能浪的浪,才是好浪!

每天 10:33 更新文章,每天掉亿点点头发...

源码精品专栏

 

ba3000727357ad4af6373c3c2186a8b0.jpeg


摘要:通过两篇原理篇的学习,我相信大家对于如何实现一个聊天系统已经有了一个大概的思路了。接下来我们就通过实战,来巩固一下这些知识点,并实现一个在线聊天系统吧。


一、前提回顾

基于 Netty 实现在线聊天系统(原理篇一)

基于 Netty 实现在线聊天系统(原理篇二)

基于 Spring Boot + MyBatis Plus + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能

  • 项目地址:https://gitee.com/zhijiantianya/ruoyi-vue-pro

  • 视频教程:https://doc.iocoder.cn/video/

二、目录介绍

  • 功能梳理

  • 具体实现

基于 Spring Cloud Alibaba + Gateway + Nacos + RocketMQ + Vue & Element 实现的后台管理系统 + 用户小程序,支持 RBAC 动态权限、多租户、数据权限、工作流、三方登录、支付、短信、商城等功能

  • 项目地址:https://gitee.com/zhijiantianya/yudao-cloud

  • 视频教程:https://doc.iocoder.cn/video/

三、需求梳理

通过前面两章内容的学习,我们基本学会了如何使用 Netty 建立一个长连接,接下来我们就在这个基础上,实现一个单机版的 im 系统。

主要功能,我梳理了一下:

  • 登录

  • 维持连接、心跳检测

  • 聊天消息

  • 消息ack

使用到的相关组件:

  • SpringBoot-job

  • GuavaCache

四、具体实现

本期的内容是基于,原理篇一的 dome 代码基础上进行的,没有看过的原理一的小伙伴,建议先回顾一下原理篇一。

原理篇一的代码结构:

  • Server(主程序)

    • ServerHandler(业务处理程序)

实战篇一的代码结构:

333f122aa795fdd1b8f9505db760fddd.png

代码的层级结构如上所示,接下来,我们将会一个个模块对逻辑进行讲解。

1、登录

1)实现逻辑

不管是长连接还是短连接,鉴权这个动作都是要有的,我相信这个功能模块,大家是很好理解的。我这里就不在过多的赘述了,具体实现步骤如下所示:

1、前后端建立 ws 连接

2、前端发送登录类型的报文,如下所示:

{
    "token": "2",
    "type": "10"
}

token:这里的 token,就是用户登录标识,大家可以根据自己所依赖的业务系统,进行修改。

type:这里表示消息报文的类型,本文所有类型定义如下所示:

  • USER_LOGIN (10, "用户上线")

  • USER_LOGIN_RESP (11, "用户上线响应")

  • HEARTBEAT_TIMEOUT (30, "心跳超时")

  • PING (40, "心跳")

  • PONG (41, "心跳响应")

  • CHAT (80, "聊天"),

  • CHAT_RESP (81, "聊天响应")

  • ACK (90, "确认")

  • ACK_RESP (91, "确认响应")

  • UNKNOWN (0, "未知类型")

示例代码如下图所示,WsMsgDispatcher.dispatch

42ee739fb16d6f0c1162cf272fd3e56f.jpeg
消息类型

3、后端对 token 进行校验,校验成功就记录用户登录信息。

示例代码如下图所示,UserLoginProcessor.login

a7f22c8f1d7a6790cd60430b41139972.jpeg
登录业务逻辑
2)具体效果

主要的业务代码我们已经讲解完毕了,接下来我们来看看效果:

002d7daa48079c92cace2831d61edef0.jpeg
用户登录

从上图,我们可以看到,我们登录的两个用户都成功了,并且返回了对应的用户信息。

2、维持连接、心跳检测

这个模块的功能,其实我们在原理篇二的时候已经讲过了具体的实现方案 了,这里也不再过多的赘述了,我们直接来看具体实现方法吧。

1)维持连接

1、前端每10秒发送一次心跳消息,报文如下所示:

{
    "type": "40",
    "fromId": "2"
}

注:前端发送的每个消息,理论上都是需要带上用户表示的,后端都是需要进行鉴权操作的。我们这里为了方便讲解(偷懒,bushi)将这部分逻辑进行了简化,大家在具体实现的时候,记得一定要加上 鉴权逻辑

2、后端检测,用户是否还在线,如果在线,则刷新用户的最新在线时间,并回复 PONG 消息。

示例代码如下图所示,HeartBeatProcessor.process

bb1b0a57588512e7403131e485c87afc.jpeg
维持连接1
bffb1737a846e669d501dee7e08f4a08.jpeg
维持连接2
2)心跳检测

这里主要是基于 IdleStateEvent 事件实现的。

TextWebSocketFrameHandler 继承 SimpleChannelInboundHandler 类,并实现 userEventTriggered 方法,具体代码如下所示:

756e5aa5489156c6300e9d39dfae455e.jpeg
心跳检测

这里详细说一下,三种事件的区别:

  • readerIdleTimeSeconds: 读超时。即当在指定的时间间隔内没有从 Channel 读取到数据时,会触发一个 READER_IDLEIdleStateEvent 事件。

  • writerIdleTimeSeconds: 写超时。即当在指定的时间间隔内没有数据写入到 Channel 时,会触发一个 WRITER_IDLEIdleStateEvent 事件。

  • allIdleTimeSeconds: 读/写超时。即当在指定的时间间隔内没有读且没有写操作时,会触发一个 ALL_IDLEIdleStateEvent 事件。

所以,我们这里检测 ALL_IDLE 事件即可。

3)具体效果

维持连接效果如下所示:

a4674576d170c3ae243c12fe8ec87de2.jpeg
维持连接效果

心跳检测效果如下所示:

9958e4f8500a55262dde1489b220bdae.jpeg
心跳超时效果1

eef3435c18dbe3fef0a70083a133a25b.jpeg心跳超时效果2

3、聊天消息

聊天消息模块主要分为两部分:

  • 消息接收:客户端推送消息到服务端

  • 消息推送:服务端将消息推送到指定的客户端

这边主要的难点在于,服务端将消息推送到指定的客户端,具体场景有2种情况:

  • 消息的发送者和消息的接受者,在同一台服务器上建立的 ws 连接,这种情况,就很好处理,直接在服务器上找到建立的 ws 连接,然后将消息推送给对应的客户端。

  • 消息的发送者和消息的接受者,在不同的服务器上建立的 ws 连接,这种情况就比较复杂,实现方案也很多,比较简单的实现方式就是,发送一条广播消息,让对应的服务器,将消息推送到指定的客户端。

本文由于是 单机版 的 im,所以只会有第一种情况发生,第二种情况就留给大家自由发挥了。

1)消息接收

具体步骤如下所示:

1、客户端发送类型为80的报文,如下所示:

{
        "type": "80",
        "fromId": "1",
        "toId": "2",
        "content": {
        "contentType": 1,
        "body": "测试消息"
        }
        }

2、服务端(ChatProcessor)对消息进行处理,具体代码如下所示:

6c30a6baee22d7fe94d972be1d63b28b.png
消息接收
2)消息推送

具体步骤如下所示:

1、获取消息接受者所连接的服务器 ip 地址 2、判断当前服务器 ip 地址是否和上面的 ip 地址相同,如果相同则推送消息,否则转发给目标服务器

具体代码如下所示:

3dbafe6edd8ada66ec5a84360372a271.png
消息推送
3)具体效果

1、我们先登录两个用户,分别是张三、李四,如下图所示:

7543f67b4f9bf4d21355f7803198c626.png
聊天登录

2、张三发送消息给李四,如下图所示:

b596ff906de8fde9a1e642c77abda877.png
张三发送消息给李四

3、李四发送消息给张三,如下图所示:

9260e78a8809a7b91c9769361a691b33.png
李四发送消息给张三

4、消息 ack

因为网络环境异常或者其他异常状况的发送,可能会出现消息推送失败的情况,这时候就需要 消息 ack 机制和重试,来保证我们的消息可以推送成功。

1)消息 ack 机制

具体步骤如下:

1、客户端收到 80 类型的消息,解析并发送 ack 报文,如下所示:

{
        "type": "90",
        "msgId": "2bfea133-72a8-4315-82aa-80049fe4fb7b"
        }

2、服务端收到 ack 消息,变更消息状态(AckProcessor),具体代码如下图所示:

ee7ac5c77d053a811ddfbc5093012eb2.png
消息ack
2)消息重试

这里因为是单机版 im,所以直接采用 SpringBoot-Job 实现,Job 代码如下所示:

d8fc4e4affad37aeb17688e95962ef73.png
消息重试

总结

好了我们实战篇一,到这里结束了,希望大家都能跟着文中的思路,具体去实现一遍,如果遇到什么问题或者文中有什么错误的地方,欢迎大家留言。谢谢观看,点个赞再走吧。



欢迎加入我的知识星球,一起探讨架构,交流源码。加入方式,长按下方二维码噢

3de8961a0ce113f6785ad051be172169.png

已在知识星球更新源码解析如下:

0b6105557e66e694b6c669fd82abcf74.jpeg

44350caaab222f0a51ad6848591a76e7.jpeg

c8b66ddefcc1d8db980a3d36df3b75d7.jpeg

eac2374ccd3828dcfd5d67398c71f8f5.jpeg

最近更新《芋道 SpringBoot 2.X 入门》系列,已经 101 余篇,覆盖了 MyBatis、Redis、MongoDB、ES、分库分表、读写分离、SpringMVC、Webflux、权限、WebSocket、Dubbo、RabbitMQ、RocketMQ、Kafka、性能测试等等内容。

提供近 3W 行代码的 SpringBoot 示例,以及超 4W 行代码的电商微服务项目。

获取方式:点“在看”,关注公众号并回复 666 领取,更多内容陆续奉上。

文章有帮助的话,在看,转发吧。
谢谢支持哟 (*^__^*)
  • 0
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
Netty入门实战: 仿写微信IM即时通讯系统》是一本关于网络编程框架Netty的实践指南。本书以仿写微信IM即时通讯系统为例,通过实际的项目案例引导读者学习和掌握Netty使用Netty是一款基于Java的网络编程框架,提供了高性能、异步、事件驱动的特性。在本书中,作者基于Netty框架,通过分析微信IM即时通讯系统的架构和功能需求,逐步引入Netty的概念和使用方法。 首先,本书介绍了Netty的基本概念和工作原理,解释了Netty的事件驱动模型以及异步IO操作的优势。接着,读者会学习到如何使用Netty构建网络服务器和客户端,以及如何处理网络通信中的数据包、编解码、心跳检测等问题。同时,本书也强调了Netty在高并发情况下的性能优势,示范了如何使用Netty进行性能优化和扩展。 通过跟随本书的实例代码,读者将逐步了解和掌握Netty的各项功能和使用方法。同时,通过仿写微信IM即时通讯系统的实践项目,读者也能够更好地理解Netty框架在实际项目中的应用场景和解决方案。 总而言之,《Netty入门实战: 仿写微信IM即时通讯系统》通过实际案例的方式,帮助读者深入理解Netty框架的使用和原理,并将其应用于实际的项目中。无论是对于新手还是有一定经验的开发者来说,本书都是一个很好的学习和实践指南,能够帮助读者快速入门和提升自己在网络编程领域的技能水平。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值