本文收作者“大白菜”分享,有改动。注意:本系列是给IM初学者的文章,IM老油条们还望海涵,勿喷!
1、引言
这又是一篇基于Netty的IM编码实践文章,因为合成一篇内容太长,读起来太累,所以也就顺着作者的思路分开成4篇,读起来心理压力也就没那么大了。
这个系列的几篇文章分享的是:假设在没有任何成型的第3方IM库或SDK的情况下,以网络编程的基础技术视野,思考和实践如何基于Netty网络库从零写一个可以聊天的IM系统的过程,没有眼花缭乱的架构设计、也没有高端大气的模式设计方法论,有的只是从IM入门者的角度的思路和实战,适合IM初学者阅读。
本篇主要是徒手撸IM系列的开篇,主要讲解的是的IM设计思路,不涉及实践编码,希望给你带来帮助。
2、知识准备
* 重要提示:本系列文章主要是代码实战分享,如果你对即时通讯(IM)技术理论了解的不多,建议先详细阅读:《零基础IM开发入门:什么是IM系统?》、《新手入门一篇就够:从零开发移动端IM》。
不知道 Netty 是什么?这里简单介绍下:
Netty 是一个 Java 开源框架。Netty 提供异步的、事件驱动的网络应用程序框架和工具,用以快速开发高性能、高可靠性的网络服务器和客户端程序。 也就是说,Netty 是一个基于 NIO 的客户、服务器端编程框架,使用Netty 可以确保你快速和简单的开发出一个网络应用,例如实现了某种协议的客户,服务端应用。 Netty 相当简化和流线化了网络应用的编程开发过程,例如,TCP 和 UDP 的 Socket 服务开发。
Netty的基础入门好文章:
- 1)新手入门:目前为止最透彻的的Netty高性能原理和框架架构解析
- 2)写给初学者:Java高性能NIO框架Netty的学习方法和进阶策略
- 3)史上最通俗Netty框架入门长文:基本介绍、环境搭建、动手实战
如果你连Java的NIO都不知道是什么,下面的文章建议优先读:
- 1)少啰嗦!一分钟带你读懂Java的NIO和经典IO的区别
- 2)史上最强Java NIO入门:担心从入门到放弃的,请读这篇!
- 3)Java的BIO和NIO很难懂?用代码实践给你看,再不懂我转行!
Netty源码和API的在线查阅地址:
- 1)Netty-4.1.x 完整源码(在线阅读版)(* 推荐)
- 2)Netty-4.1.x API文档(在线版)
3、系列文章
本文是系列文章的第1篇,以下是系列目录:
- 《基于Netty,徒手撸IM(一):IM系统设计篇》(* 本文)
- 《基于Netty,徒手撸IM(二):编码实践篇(单聊功能)》
- 《基于Netty,徒手撸IM(三):编码实践篇(群聊功能)》
- 《基于Netty,徒手撸IM(一):编码实践篇(系统优化)》
4、需求分析
业务场景: 本次实战就是模拟微信的IM聊天,每个客户端和服务端建立连接,并且可以实现点对点通信(单聊),点对多点通信(群聊)。
设计思路: 我们要实现的是点(客户端)对点(客户端)的通讯,但是我们大部分情况下接触的业务都是客户端和服务端之间的通讯(所谓的C/S模式?),客户端只需要知道服务端的 IP 地址和端口号即可发起通讯了。那么客户端和客户端应该怎么去设计呢?
技术思考:难道是手机和手机之间建立通讯连接(所谓的P2P),互相发送消息吗?
这种方案显然不是很好的方案:
- 1)首先: 客户端和客户端之间通讯,首先需要确定对方的 IP 地址和端口号,显然不是很现实;
- 2)其次: 即使有办法拿到对方的 IP 地址和端口号,那么每个点(客户端)既作为服务端还得作为客户端,无形之中增加了客户端的压力。
其实:我们可以使用服务端作为IM聊天消息的中转站,由服务端主动往指定客户端推送消息。如果是这种模式的话,那么 Http 协议是无法支持的(因为Http 是无状态的,只能一请求一响应的模式),于是就只能使用 TCP 协议去实现了。
Jack Jiang注:此处作者表述不太准确,因为虽然HTTP是无状态的,但一样可以实现即时通讯能力,有兴趣的读者可以阅读以下几篇文章,了解一下这些曾经利用HTTP实现即时通讯聊天的技术方法:
- 《新手入门贴:史上最全Web端即时通讯技术原理详解》
- 《Web端即时通讯技术盘点:短轮询、Comet、Websocket、SSE》
- 《网页端IM通信技术快速入门:短轮询、长轮询、SSE、WebSocket》
5、IM单聊思路设计
5.1 通讯架构原理
以下是通讯架构原理图:
如上图所示,通讯流程解析如下:
- 1)实现客户端和客户端之间通讯,那么需要使用服务端作为通讯的中转站,每个客户端都必须和服务端建立连接;
- 2)每个客户端和服务端建立连接之后,服务端保存用户 ID 和通道的映射关系,其中用户 ID 作为客户端的唯一标识;
- 3)客户端 A 往客户端 B 发送消息时,先把消息发送到服务端,再有服务端往客户端 B 进行推送。
针对上述第“3)”点,服务端如何找到客户端 B 呢?
客户端 A 往服务端发送消息时,消息携带的信息有:“客户端 A 用户 ID”、“客户端 B 用户 ID”、“消息内容”。这样服务端就能顺利找到服务端 B 的通道并且进行推送消息了。
5.2 消息推送流程
每个客户端和服务端建立连接的时候,必须把个人用户信息上传到服务端,由服务端统一保存映射关系。如果某个客户端下线了,则服务端监听到连接断开,删除对应的映射关系。
其次:发起群聊的时候,需要传递 touser 字段,服务端根据该字段在映射表里面查找到对应的连接通道并发起消息推送。
上述逻辑原理如下图所示:
5.3 更多的细节
其实在真正要做IM之前,要考虑的技术细节还是很多的,以下这几篇文章就步及到了典型的几个IM热门技术点,有兴趣的一定要读一读:
- 《移动端IM开发需要面对的技术问题》
- 《谈谈移动端 IM 开发中登录请求的优化》
- 《IM消息送达保证机制实现(一):保证在线实时消息的可靠投递》
- 《IM消息送达保证机制实现(二):保证离线消息的可靠投递》
- 《如何保证IM实时消息的“时序性”与“一致性”?》
6、IM群聊思路设计
群聊指的是一个组内多个用户之间的聊天,一个用户发到群组的消息会被组内任何一个成员接收 。
具体架构思路如下所示:
如上图所示,群聊通讯流程解析如下。
1)群聊其实和单聊整体上思路都是一致的,都是需要保存每个用户和通道的对应关系,方便后期通过用户 ID 去查找到对应的通道,再跟进通道推送消息。
2)如何把消息发送给多个组内的成员呢?
其实很简单,服务端再保存另外一份映射关系,那就是聊天室和成员的映射关系。发送消息时,首先根据聊天室 ID 找到对应的所有成员,然后再跟进各个成员的 ID 去查找到对应的通道,最后由每个通道进行消息的发送。
3)成员加入某个群聊组的时候,往映射表新增一条记录,如果成员退群的时候则删除对应的映射记录。
通过上面的架构图可以发现,群聊和单聊相比,其实就是多了一份映射关系而已。
其实群聊是IM里相对来说技术难度较高的功能,有兴趣的读者可以阅读下面这几篇:
- 《IM单聊和群聊中的在线状态同步应该用“推”还是“拉”?》
- 《IM群聊消息如此复杂,如何保证不丢不重?》
- 《移动端IM中大规模群消息的推送如何保证效率、实时性?》
- 《现代IM系统中聊天消息的同步和存储方案探讨》
- 《关于IM即时通讯群聊消息的乱序问题讨论》
- 《IM群聊消息的已读回执功能该怎么实现?》
- 《IM群聊消息究竟是存1份(即扩散读)还是存多份(即扩散写)?》
- 《一套高可用、易伸缩、高并发的IM群聊、单聊架构方案设计实践》
7、本文小结
本篇主要是帮助读者掌握单聊和群聊的核心设计思路。
单聊: 主要是服务器保存了一份用户和通道之间的映射关系,发送消息的时候,根据接收人 ID 找到其对应的通道 Channel,Channel 的 write () 可以给客户端发送消息。
群聊: 保存两份关系,分别是用户 ID 和 Channel 之间的关系、群组 ID 和用户 ID 的关系。推送消息的时候,首先根据聊天组 ID 找到其对应的成员,遍历每个成员再进行找出其对应的通道即可。
整体来说,思路还是很简单的,掌握了该设计思路以后,你会发现设计一款 IM 聊天软件其实也不是很复杂。
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长连接技术实践:技术选型、架构设计、性能优化
(本文已同步发布于:http://www.52im.net/thread-3963-1-1.html)