默认
打赏 发表评论 8
想开发IM:买成品怕坑?租第3方怕贵?找开源自已撸?尽量别走弯路了... 找站长给点建议
基于Netty,从零开发IM(一):IM系统设计篇
阅读(73578) | 评论(8 收藏3 淘帖2 2
微信扫一扫关注!

本文由作者“大白菜”分享,个人博客 cmsblogs.cn,即时通讯收录时有改动。注意:本系列是给IM初学者的文章,IM老油条们还望海涵,勿喷!


1、引言


这又是一个基于Netty的IM编码实践系列文章,因为合成一篇内容太长,读起来太累,所以也就顺着作者的思路分开成4篇,读起来心理压力也就没那么大了。

这个系列的几篇文章分享的是:假设在没有任何成型的第3方IM库或SDK的情况下,以网络编程的基础技术视野,思考和实践如何基于Netty网络库从零写一个可以聊天的IM系统的过程,没有眼花缭乱的架构设计、也没有高端大气的模式设计方法论,有的只是从IM入门者的角度的思路和实战,适合IM初学者阅读

本篇主要是徒手撸IM系列的开篇,主要讲解的是的IM设计思路,不涉及实践编码,希望给你带来帮助。

基于Netty,从零开发IM(一):IM系统设计篇_cover-opti.png

2、知识准备


* 重要提示:本系列文章主要是代码实战分享,如果你对即时通讯(IM)技术理论了解的不多,建议先详细阅读:《零基础IM开发入门:什么是IM系统?》、《新手入门一篇就够:从零开发移动端IM》。

基于Netty,从零开发IM(一):IM系统设计篇_x.jpg

不知道 Netty 是什么?这里简单介绍下:

Netty 是一个 Java 开源框架。Netty 提供异步的、事件驱动的网络应用程序框架和工具,用以快速开发高性能、高可靠性的网络服务器和客户端程序。
也就是说,Netty 是一个基于 NIO 的客户、服务器端编程框架,使用Netty 可以确保你快速和简单的开发出一个网络应用,例如实现了某种协议的客户,服务端应用。
Netty 相当简化和流线化了网络应用的编程开发过程,例如,TCP 和 UDP 的 Socket 服务开发。


Netty的基础入门好文章:


如果你连Java的NIO都不知道是什么,下面的文章建议优先读:


Netty源码和API的在线查阅地址:


3、系列文章


本文是系列文章的第1篇,以下是系列目录:


4、需求分析


业务场景: 本次实战就是模拟微信的IM聊天,每个客户端和服务端建立连接,并且可以实现点对点通信(单聊),点对多点通信(群聊)。

设计思路: 我们要实现的是点(客户端)对点(客户端)的通讯,但是我们大部分情况下接触的业务都是客户端和服务端之间的通讯(所谓的C/S模式?),客户端只需要知道服务端的 IP 地址和端口号即可发起通讯了。那么客户端和客户端应该怎么去设计呢?

技术思考:难道是手机和手机之间建立通讯连接(所谓的P2P),互相发送消息吗?

这种方案显然不是很好的方案:

  • 1)首先: 客户端和客户端之间通讯,首先需要确定对方的 IP 地址和端口号,显然不是很现实;
  • 2)其次: 即使有办法拿到对方的 IP 地址和端口号,那么每个点(客户端)既作为服务端还得作为客户端,无形之中增加了客户端的压力。

其实:我们可以使用服务端作为IM聊天消息的中转站,由服务端主动往指定客户端推送消息。如果是这种模式的话,那么 Http 协议是无法支持的(因为Http 是无状态的,只能一请求一响应的模式),于是就只能使用 TCP 协议去实现了。

Jack Jiang注:此处作者表述不太准确,因为虽然HTTP是无状态的,但一样可以实现即时通讯能力,有兴趣的读者可以阅读以下几篇文章,了解一下这些曾经利用HTTP实现即时通讯聊天的技术方法:


5、IM单聊思路设计


5.1通讯架构原理


以下是通讯架构原理图:
基于Netty,从零开发IM(一):IM系统设计篇_1.png

如上图所示,通讯流程解析如下:

  • 1)实现客户端和客户端之间通讯,那么需要使用服务端作为通讯的中转站,每个客户端都必须和服务端建立连接;
  • 2)每个客户端和服务端建立连接之后,服务端保存用户 ID 和通道的映射关系,其中用户 ID 作为客户端的唯一标识;
  • 3)客户端 A 往客户端 B 发送消息时,先把消息发送到服务端,再有服务端往客户端 B 进行推送。

针对上述第“3)”点,服务端如何找到客户端 B 呢?

客户端 A 往服务端发送消息时,消息携带的信息有:“客户端 A 用户 ID”、“客户端 B 用户 ID”、“消息内容”。这样服务端就能顺利找到服务端 B 的通道并且进行推送消息了。

5.2消息推送流程


每个客户端和服务端建立连接的时候,必须把个人用户信息上传到服务端,由服务端统一保存映射关系。如果某个客户端下线了,则服务端监听到连接断开,删除对应的映射关系。

其次:发起群聊的时候,需要传递 touser 字段,服务端根据该字段在映射表里面查找到对应的连接通道并发起消息推送。

上述逻辑原理如下图所示:
基于Netty,从零开发IM(一):IM系统设计篇_2.png

5.3更多的细节


其实在真正要做IM之前,要考虑的技术细节还是很多的,以下这几篇文章就步及到了典型的几个IM热门技术点,有兴趣的一定要读一读:


6、IM群聊思路设计


群聊指的是一个组内多个用户之间的聊天,一个用户发到群组的消息会被组内任何一个成员接收 。

具体架构思路如下所示:
基于Netty,从零开发IM(一):IM系统设计篇_3.png

如上图所示,群聊通讯流程解析如下。

1)群聊其实和单聊整体上思路都是一致的,都是需要保存每个用户和通道的对应关系,方便后期通过用户 ID 去查找到对应的通道,再跟进通道推送消息。

2)如何把消息发送给多个组内的成员呢?

其实很简单,服务端再保存另外一份映射关系,那就是聊天室和成员的映射关系。发送消息时,首先根据聊天室 ID 找到对应的所有成员,然后再跟进各个成员的 ID 去查找到对应的通道,最后由每个通道进行消息的发送。

3)成员加入某个群聊组的时候,往映射表新增一条记录,如果成员退群的时候则删除对应的映射记录。

通过上面的架构图可以发现,群聊和单聊相比,其实就是多了一份映射关系而已。

其实群聊是IM里相对来说技术难度较高的功能,有兴趣的读者可以阅读下面这几篇:


另外,对于超大规模群聊,技术难度更是指数上升:


7、本文小结


本篇主要是帮助读者掌握单聊和群聊的核心设计思路。

单聊: 主要是服务器保存了一份用户和通道之间的映射关系,发送消息的时候,根据接收人 ID 找到其对应的通道 Channel,Channel 的 write () 可以给客户端发送消息。

群聊: 保存两份关系,分别是用户 ID 和 Channel 之间的关系、群组 ID 和用户 ID 的关系。推送消息的时候,首先根据聊天组 ID 找到其对应的成员,遍历每个成员再进行找出其对应的通道即可。

整体来说,思路还是很简单的,掌握了该设计思路以后,你会发现设计一款 IM 聊天软件其实也不是很复杂。

8、相关文章


如果你觉得对本系列文章还不够详细,可以系统学习以下系列文章:


9、参考资料


[1] 新手入门:目前为止最透彻的的Netty高性能原理和框架架构解析
[2] 理论联系实际:一套典型的IM通信协议设计详解
[3] 浅谈IM系统的架构设计
[4] 简述移动端IM开发的那些坑:架构设计、通信协议和客户端
[5] 一套海量在线用户的移动端IM架构设计实践分享(含详细图文)
[6] 一套原创分布式即时通讯(IM)系统理论架构方案
[7]  一套高可用、易伸缩、高并发的IM群聊、单聊架构方案设计实践
[8] 一套亿级用户的IM架构技术干货(上篇):整体架构、服务拆分等
[9] 一套亿级用户的IM架构技术干货(下篇):可靠性、有序性、弱网优化等
[10] 从新手到专家:如何设计一套亿级消息量的分布式IM系统
[11] 基于实践:一套百万消息量小规模IM系统技术要点总结
[12] 探探的IM长连接技术实践:技术选型、架构设计、性能优化

即时通讯网 - 即时通讯开发者社区! 来源: - 即时通讯开发者社区!

上一篇:一套十万级TPS的IM综合消息系统的架构实践与思考下一篇:基于Netty,从零开发IM(二):编码实践篇(单聊功能)

本帖已收录至以下技术专辑

推荐方案
评论 8
赞!期待后续~~~
签名: 好好学习,天天向上
引用:菜小柒 发表于 2022-07-08 23:40
赞!期待后续~~~

im初学者?
期待后续,希望尽快把剩下的都发出来
引用:zikong 发表于 2022-07-10 21:08
期待后续,希望尽快把剩下的都发出来

正在编辑,明天继续发下一篇
建议增加一个自定义私有协议的定义说明,一般这种项目都是私有协议。
学习了
打赏楼主 ×
使用微信打赏! 使用支付宝打赏!

返回顶部