Jack Jiang

我的最新工程MobileIMSDK:http://git.oschina.net/jackjiang/MobileIMSDK
posts - 295, comments - 13, trackbacks - 0, articles - 0

2022年6月20日

     摘要: 本文引用自InfoQ社区“5亿用户如何高效沟通?钉钉首次对外揭秘即时消息服务DTIM”一文,作者陈万红等、策划褚杏娟,有修订和改动。一、引言本文是国内企业IM的事实王者钉钉首次对外深度解密其即时消息服务(即DingTalk IM,简称DTIM)的技术设计实践。本篇文章内容将从模型设计原理到具体的技术架构、最底层的存储模型到跨地域的单元化等,全方位展现了 DTIM 在实际生产...  阅读全文

posted @ 2022-08-15 12:32 Jack Jiang 阅读(61) | 评论 (0)编辑 收藏

     摘要: 本文由vivo互联网服务器团队李青鑫分享,有较多修订和改动。1、引言本文内容来自vivo互联网服务器团队李青鑫在“2021 vivo开发者大会”现场的演讲内容整理而成(现场演讲稿可从本文末附件中下载)。本文将要分享的是手机厂商vivo的系统级推送平台在架构设计上的技术实践和总结。这也是目前为止首次由手机厂商分享的自建系统级推送平台的技术细节,我们也得以借此机会一窥厂商ROO...  阅读全文

posted @ 2022-08-09 12:11 Jack Jiang 阅读(58) | 评论 (0)编辑 收藏

一、关于RainbowChat-Web

RainbowChat-Web是一套Web网页端IM系统,是RainbowChat的姊妹产品(RainbowChat是一套基于开源IM聊天框架 MobileIMSDK(Github地址) 的产品级移动端IM系统)。

不同于市面上某些开源或淘宝售卖的demo级代码,RainbowChat-Web的产品级代码演化自真正运营过的商业产品,其所依赖的通信层核心SDK(即MobileIMSDK-Web)已在数年内经过大量客户及其辐射的最终用户的使用和验证。

二、v4.1 版更新内容

此版更新内容更多历史更新日志):

  • 1)[bug][前端]解决了掉线后发出的消息,在被判定未送达的情况下,重连成功时会再次重发的问题(这是MobileIMSDK-Web的bug);
  • 2)[优化][前端]解决了发送的html等内容,对方显示正常,而自已这边显示不正常的问题(没被转义);
  • 3)[优化][服务端-独立交付版]解决了log4j2的两个jar包冲突导致在linux下不能正常输出log的问题;
  • 4)[优化][服务端-RainbowChatMQserver]优化了使用mysql8.0驱动时,不能正确读取SQL异常信息的问题(会报空指针异常);
  • 5)[优化][前端]解决了位置消息发送功能无法正常使用的问题(高德地图官方API升级,已适配并升级完成);
  • 6)[优化][前端]解决了位置消息查看时的地图控制工具不正常的问题(高德地图官方API升级,已适配并升级完成)。

升级后的位置消息相关功能截图(更多截图点此查看):

三、关于兼容性

截止目前:RainbowChat-Web努力保证在各主流系统、主流浏览器、不同分辨率屏幕上的一致体验,包括但不限于:Chrome、Safari、FireFox、Edge、360浏览器、世界之窗浏览器等▼

▲ 在各种主流浏览器上的运行情况更多截图点此进入更多演示视频点此进入

▲ 超宽屏上的显示情况更多截图点此进入更多演示视频点此进入

 

▲ 不同系统、不同分辨率屏幕的真机运行情况更多截图点此进入更多演示视频点此进入) 

四、主要界面截图概览

 

▲ 主界面更多截图点此进入更多演示视频点此进入

▲ 主界面(聊天窗全屏时)更多截图点此进入更多演示视频点此进入

▲ 主界面(聊天窗关闭时)更多截图点此进入更多演示视频点此进入

posted @ 2022-08-06 12:14 Jack Jiang 阅读(43) | 评论 (0)编辑 收藏

     摘要: 本文由vivo互联网技术团队LinDu、Li Guolin分享,有较多修订和改动。1、引言IM即时消息模块是直播系统的重要组成部分,一个稳定、有容错、灵活的、支持高并发的消息模块是影响直播系统用户体验的重要因素。本文针对秀场直播,结合我们一年以来通过处理不同的业务线上问题,进行了技术演进式的IM消息模块架构的升级与调整,并据此进行了技术总结、整理成文,希望借此机会分享给大家。在目前大部分主流的直播...  阅读全文

posted @ 2022-08-01 12:37 Jack Jiang 阅读(64) | 评论 (0)编辑 收藏

本文由作者“大白菜”分享,有较多修订和改动。注意:本系列是给IM初学者的文章,IM老油条们还望海涵,勿喷!

1、引言

前两篇《编码实践篇(单聊功能)》、《编码实践篇(群聊功能)》分别实现了控制台版本的IM单聊和群聊的功能。

通过前两篇这两个小案例来体验的只是Netty在IM系统这种真实的开发实践,但对比在真实的Netty应用开发当中,本系列的案例是非常的简单的,主要目的其实是让大家可以更好地了解其原理,从而写出更高质量的 Netty 代码。

不过,虽然 Netty 的性能很高,但是也不能保证随意写出来的项目就是性能很高的,所以本篇将主要讲解几个基于Netty的IM系统的优化实战技术点。

学习交流:

本文同步发布于:http://www.52im.net/thread-3988-1-1.html

2、写在前面

建议你在阅读本文之前,务必先读本系列的前三篇《IM系统设计篇》、《编码实践篇(单聊功能)》、《编码实践篇(群聊功能)》。

最后,在开始本文之前,请您务必提前了解Netty的相关基础知识,可从本系列首篇《IM系统设计篇》中的“知识准备”一章开始。

3、系列文章

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

4、基于Netty的IM系统常见优化方向

常见优化方向脑图:

我们逐条详细解释一下这些优化的目的:

  • 1)心跳检测:主要是避免连接假死现象;
  • 2)连接断开:则删除通道绑定属性、删除对应的映射关系,这些信息都是保存在内存当中的,如果不删除则造成资源浪费;
  • 3)性能问题:用户 ID 和 Channel 的关系绑定存在内存当中,比如:Map,key 是用户 ID,value 是 Channel,如果用户量多的情况(客户端数量过多),那么服务端的内存将被消耗殆尽;
  • 4)性能问题:每次服务端往客户端推送消息,都需从Map里查找到对应的Channel,如果数量较大和查询频繁的情况下如何保证查询性能;
  • 5)安全问题:HashMap 是线程不安全的,并发情况下,我们如何去保证线程安全;
  • 6)身份校验:如何 LoginHandler 是负责登录认证的业务 Handler,AuthHandler 是负责每次请求时校验该请求是否已经认证了,这些 Handler 在链接就绪时已经被添加到 Pipeline 管道当中,其实,我们可以采用热插拔的方式去把一些在做业务操作时用不到的 Handler 给剔除掉。

以上是基于Netty的IM系统开发当中,需要去注意的技术优化点,当然还有很多其他的细节,比如:线程池这块,需要大家慢慢去从实战中积累。

5、本篇优化方向

本篇主要的优化内容主要是在第二篇单聊功能第三篇群聊功能的基础上继续完善几点。

具体的优化方向如下:

  • 1)无论客户端还是服务端都分别只有一个 Handler,这样的话,业务越来越多,Handler 里面的代码就会越来越臃肿,我们应该想办法把 Handler 拆分成各个独立的 Handler;
  • 2)如果拆分的 Handler 很多,每次有连接进来,那么都会触发 initChannel () 方法,所有的 Handler 都得被 new 一遍,我们应该把这些 Handler 改成单例模式(不需要每次都 new,提高效率);
  • 3)发送消息时,无论是单聊还是群聊,对方不在线,则把消息缓存起来,等待其上线再推送给他;
  • 4)连接断开时,无论是主动和被动,需要删除 Channel 属性、删除用户和 Channel 映射关系。

6、业务拆分以及单例模式优化

6.1 概述

主要优化细节如下:

  • 1)自定义 Handler 继承 SimpleChannelInboundHandler,那么解码的时候,会自动根据数据格式类型转到相应的 Handler 去处理;
  • 2)@Shareable 修饰 Handler,保证 Handler 是可共享的,避免每次都创建一个实例。

6.2 登录Handler优化

@ChannelHandler.Sharable

public class ClientLogin2Handler extends SimpleChannelInboundHandler<LoginResBean> {

    //1.构造函数私有化,避免创建实体

    private ClientLogin2Handler(){}

    //2.定义一个静态全局变量

    public static ClientLogin2Handler instance=null;

    //3.获取实体方法

    public static ClientLogin2Handler getInstance(){

        if(instance==null){

            synchronized(ClientLogin2Handler.class){

                if(instance==null){

                    instance=new ClientLogin2Handler();

                }

            }

        }

        return instance;

    }

 

    protected void channelRead0(

        ChannelHandlerContext channelHandlerContext,

        LoginResBean loginResBean) throws Exception {

 

        //具体业务代码,参考之前

    }

}

6.3 消息发送Handler优化

@ChannelHandler.Sharable

public class ClientMsgHandler extends SimpleChannelInboundHandler<MsgResBean> {

    //1.构造函数私有化,避免创建实体

    private ClientMsgHandler(){}

    //2.定义一个静态全局变量

    public static ClientMsgHandler instance=null;

    //3.获取实体方法

    public static ClientMsgHandler getInstance(){

        if(instance==null){

            synchronized(ClientMsgHandler.class){

                if(instance==null){

                    instance=new ClientMsgHandler();

                }

            }

        }

        return instance;

    }

 

    protected void channelRead0(

        ChannelHandlerContext channelHandlerContext,

        MsgResBean msgResBean) throws Exception {

 

        //具体业务代码,参考之前

    }

}

6.4 initChannel方法优化

.handler(newChannelInitializer<SocketChannel>() {

    @Override

    public void initChannel(SocketChannel ch) {

        //1.拆包器

        ch.pipeline().addLast(new LengthFieldBasedFrameDecoder(Integer.MAX_VALUE,5,4));

        //2.解码器

        ch.pipeline().addLast(new MyDecoder());

        //3.登录Handler,使用单例获取

        ch.pipeline().addLast(ClientLogin2Handler.getInstance());

        //4.消息发送Handler,使用单例获取

        ch.pipeline().addLast(ClientMsgHandler.getInstance());

        //5.编码器

        ch.pipeline().addLast(new MyEncoder());

    }

});

6.5 小结

这种业务拆分以及单例模式优优化是Netty开发当中很常用的,可以更好的维护基于Netty的代码并提高应用性能。

7、数据缓存优化

为了提高用户体验,在发送消息(推送消息)时,如果接收方不在线,则应该把消息缓存起来,等对方上线时,再推送给他。

7.1 数据缓存到集合

//1.定义一个集合存放数据(真实项目可以存放数据库或者redis缓存),这样数据比较安全。

private List<Map<Integer,String>> datas=new ArrayList<Map<Integer,String>>();

 

//2.服务端推送消息

private void pushMsg(MsgReqBean bean,Channel channel){

    Integer touserid=bean.getTouserid();

    Channel c=map.get(touserid);

 

    if(c==null){//对方不在线

        //2.1存放到list集合

        Map<Integer,String> data=new HashMap<Integer, String>();

        data.put(touserid,bean.getMsg());

        datas.add(data);

 

        //2.2.给消息“发送人”响应

        MsgResBean res=new MsgResBean();

        res.setStatus(1);

        res.setMsg(touserid+">>>不在线");

        channel.writeAndFlush(res);

 

    }else{//对方在线

        //2.3.给消息“发送人”响应

        MsgResBean res=new MsgResBean();

        res.setStatus(0);

        res.setMsg("发送成功);

        channel.writeAndFlush(res);

 

        //2.4.给接收人推送消息

        MsgRecBean res=new MsgRecBean();

        res.setFromuserid(bean.getFromuserid());

        res.setMsg(bean.getMsg());

        c.writeAndFlush(res);

    }

}

7.2 上线推送

private void login(LoginReqBean bean, Channel channel){

    Channel c=map.get(bean.getUserid());

    LoginResBean res=new LoginResBean();

    if(c==null){

        //1.添加到map

        map.put(bean.getUserid(),channel);

        //2.给通道赋值

        channel.attr(AttributeKey.valueOf("userid")).set(bean.getUserid());

        //3.登录响应

        res.setStatus(0);

        res.setMsg("登录成功");

        res.setUserid(bean.getUserid());

        channel.writeAndFlush(res);

 

        //4.根据user查找是否有尚未推送消息

        //思路:根据userid去lists查找.......

 

    }else{

        res.setStatus(1);

        res.setMsg("该账户目前在线");

        channel.writeAndFlush(res);

    }

}

8、连接断开事件处理优化

如果客户端网络故障导致连接断开了(非主动下线),那么服务端就应该能监听到连接的断开,且此时应删除对应的 map 映射关系。但是映射关系如果没有删除掉,将导致服务器资源没有得到释放,进而影响客户端的下次同一个账号登录以及大量的客户端掉线时性能。

8.1 正确写法

实例:

public class ServerChatGroupHandler extends ChannelInboundHandlerAdapter {

    //映射关系

    private static Map<Integer, Channel> map=new HashMap<Integer, Channel>();

    //连接断开,触发该事件

    @Override

    public void channelInactive(ChannelHandlerContext ctx) throws Exception {

        //1.获取Channel

        Channel channel=ctx.channel();

 

        //2.从map里面,根据Channel找到对应的userid

        Integer userid=null;

        for(Map.Entry<Integer, Channel> entry : map.entrySet()){

            Integer uid=entry.getKey();

            Channel c=entry.getValue();

            if(c==channel){

                userid=uid;

            }

        }

        //3.如果userid不为空,则需要做以下处理

        if(userid!=null){

            //3.1.删除映射

            map.remove(userid);

            //3.2.移除标识

            ctx.channel().attr(AttributeKey.valueOf("userid")).remove();

        }

    }

}

8.2 错误写法

Channel 断开,服务端监听到连接断开事件,但是此时 Channel 所绑定的属性已经被移除掉了,因此这里无法直接获取的到 userid。

实例:

public class ServerChatGroupHandler extends ChannelInboundHandlerAdapter {

    //映射关系

    private static Map<Integer, Channel> map=new HashMap<Integer, Channel>();

 

    //连接断开,触发该事件

    @Override

    public void channelInactive(ChannelHandlerContext ctx) throws Exception {

        //1.获取Channel绑定的userid

        Object userid=channel.attr(AttributeKey.valueOf("userid")).get();

 

        //2.如果userid不为空

        if(userid!=null){

            //1.删除映射

            map.remove(userid);

            //2.移除标识

            ctx.channel().attr(AttributeKey.valueOf("userid")).remove();

        }

    }

}

9、本篇小结

本篇内容还是相对容易理解的,主要是优化前面两篇实现的IM聊天功能,优化内容是业务 Handler 的拆分以及使用单例模式、接受人不在线则缓存数据、等其上线再推送、监听连接断开删除对应的映射关系。

限于篇幅,本系列文章文章没办法真正讲解开发一个完整IM系统所涉及的方方面面,如果有兴趣,可以继续阅读更有针对性的IM开发文章,比如IM架构设计IM通信协议IM通信安全群聊优化弱网优化网络保活等。

10、参考资料

[1] 新手入门:目前为止最透彻的的Netty高性能原理和框架架构解析

[2] 理论联系实际:一套典型的IM通信协议设计详解

[3] 浅谈IM系统的架构设计

[4] 简述移动端IM开发的那些坑:架构设计、通信协议和客户端

[5] 一套海量在线用户的移动端IM架构设计实践分享(含详细图文)

[6] 一套原创分布式即时通讯(IM)系统理论架构方案

[7]  一套高可用、易伸缩、高并发的IM群聊、单聊架构方案设计实践

[8] 一套亿级用户的IM架构技术干货(上篇):整体架构、服务拆分等

[9] 从新手到专家:如何设计一套亿级消息量的分布式IM系统

[10] 基于实践:一套百万消息量小规模IM系统技术要点总结

[11] 探探的IM长连接技术实践:技术选型、架构设计、性能优化

[12] 拿起键盘就是干,教你徒手开发一套分布式IM系统

[13] 万字长文,手把手教你用Netty打造IM聊天

[14] 基于Netty实现一套分布式IM系统

[15] SpringBoot集成开源IM框架MobileIMSDK,实现即时通讯IM聊天功能

本文同步发布于:http://www.52im.net/thread-3988-1-1.html

posted @ 2022-07-25 12:02 Jack Jiang 阅读(66) | 评论 (0)编辑 收藏

一、更新内容简介

本次更新为次要版本更新,进行了若干优化(更新历史详见:码云 Release Nodes)。可能是市面上唯一同时支持 UDP+TCP+WebSocket 三种协议的同类开源IM框架。

二、MobileIMSDK简介

MobileIMSDK 是一套专为移动端开发的原创IM通信层框架:

  • 历经8年、久经考验;
  • 超轻量级、高度提炼,lib包50KB以内;
  • 精心封装,一套API同时支持UDP、TCP、WebSocket三种协议(可能是全网唯一开源的);
  • 客户端支持 iOSAndroid标准JavaH5小程序(开发中..)、Uniapp(开发中..);
  • 服务端基于Netty,性能卓越、易于扩展;👈
  • 可与姊妹工程 MobileIMSDK-Web 无缝互通实现网页端聊天或推送等;👈
  • 可应用于跨设备、跨网络的聊天APP、企业OA、消息推送等各种场景。

MobileIMSDK工程始于2013年10月,起初用作某产品的即时通讯底层实现,完全从零开发,技术自主可控!

您可能需要:查看关于MobileIMSDK的详细介绍

三、代码托管同步更新

OsChina.net

GitHub.com

四、MobileIMSDK设计目标

让开发者专注于应用逻辑的开发,底层复杂的即时通讯算法交由SDK开发人员,从而解偶即时通讯应用开发的复杂性。

五、MobileIMSDK框架组成

整套MobileIMSDK框架由以下5部分组成:

  1. Android客户端SDK:用于Android版即时通讯客户端,支持Android 2.3及以上,查看API文档
  2. iOS客户端SDK:用于开发iOS版即时通讯客户端,支持iOS 8.0及以上,查看API文档
  3. Java客户端SDK:用于开发跨平台的PC端即时通讯客户端,支持Java 1.6及以上,查看API文档
  4. H5客户端SDK:暂无开源版,查看精编注释版
  5. 服务端SDK:用于开发即时通讯服务端,支持Java 1.7及以上版本,查看API文档

整套MobileIMSDK框架的架构组成:

 另外:MobileIMSDK可与姊妹工程 MobileIMSDK-Web 无缝互通,从而实现Web网页端聊天或推送等。

六、MobileIMSDK v6.2更新内容 

【重要说明】:

MobileIMSDK v6.2 为次要版本,进行了若干优化! 查看详情

【新增的特性】:

  1. [服务端] 新增两个聊天消息前置处理回调,方便开发者进行内容鉴黄、过滤、修改等运营管理;
  2. [服务端] 新增新增了一个与 Web 互通情况下的 C2C 模式回调,用于开发者在互通模式下实现离线消息 Push 逻辑;

【其它优化和提升】:

  1. [Andriod] 支持最新的 Andriod 12,解决了 Demo 工程中的 Andriod12 兼容问题;
  2. [Andriod] 解决了 Demo 工程在最新 Android Studio 编译时报方法数超过 65535 的经典问题;
  3. [服务端] 升级 log4j2 至 2.17.0,解决 Log4j2 远程代码执行高危漏洞;
  4. [服务端] 为 ServerEventListener 类中的 onUserLogout 回调增加 beKickoutCode 参数;
  5. [服务端] [优化] 尝试解决与 Web 互通情况下,MQProvider 中的 work 方法会因异步消息导致的 AlreadCloseException 问题;

【版本地址】:

https://gitee.com/jackjiang/MobileIMSDK/releases/6.2

posted @ 2022-07-20 10:29 Jack Jiang 阅读(56) | 评论 (0)编辑 收藏

     摘要: 本文由作者“大白菜”分享,有较多修订和改动。注意:本系列是给IM初学者的文章,IM老油条们还望海涵,勿喷!1、引言接上两篇《IM系统设计篇》、《编码实践篇(单聊功能)》,本篇主要讲解的是通过实战编码实现IM的群聊功能,内容涉及群聊技术实现原理、编码实践等知识。学习交流:- 移动端IM开发入门文章:《新手入门一篇就够:从零开发移动端IM》- 开源IM框架源码:https://...  阅读全文

posted @ 2022-07-18 15:06 Jack Jiang 阅读(62) | 评论 (0)编辑 收藏

     摘要: 本文由作者“大白菜”分享,个人博客 cmsblogs.cn,有较多修订和改动。注意:本系列是给IM初学者的文章,IM老油条们还望海涵,勿喷!1、引言接上篇《IM系统设计篇》,本篇主要讲解的是通过实战编码实现IM的单聊功能,内容涉及技术原理、编码实践。补充说明:因为本系列文章主要目的是引导IM初学者在基于Netty的情况下,如何一步一步从零写出IM的逻辑和思维能力,因而为了简...  阅读全文

posted @ 2022-07-11 11:39 Jack Jiang 阅读(55) | 评论 (0)编辑 收藏

本文收作者“大白菜”分享,有改动。注意:本系列是给IM初学者的文章,IM老油条们还望海涵,勿喷!

1、引言

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

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

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

学习交流:

本文已同步发布于:http://www.52im.net/thread-3963-1-1.html

2、知识准备

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

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

Netty 是一个 Java 开源框架。Netty 提供异步的、事件驱动的网络应用程序框架和工具,用以快速开发高性能、高可靠性的网络服务器和客户端程序。

也就是说,Netty 是一个基于 NIO 的客户、服务器端编程框架,使用Netty 可以确保你快速和简单的开发出一个网络应用,例如实现了某种协议的客户,服务端应用。

Netty 相当简化和流线化了网络应用的编程开发过程,例如,TCP 和 UDP 的 Socket 服务开发。

Netty的基础入门好文章:

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

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

3、系列文章

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

  1. 基于Netty,徒手撸IM(一):IM系统设计篇》(* 本文
  2. 《基于Netty,徒手撸IM(二):编码实践篇(单聊功能)》
  3. 《基于Netty,徒手撸IM(三):编码实践篇(群聊功能)》
  4. 《基于Netty,徒手撸IM(一):编码实践篇(系统优化)》

4、需求分析

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

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

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

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

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

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

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

  1. 新手入门贴:史上最全Web端即时通讯技术原理详解
  2. Web端即时通讯技术盘点:短轮询、Comet、Websocket、SSE
  3. 网页端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热门技术点,有兴趣的一定要读一读:

  1. 移动端IM开发需要面对的技术问题
  2. 谈谈移动端 IM 开发中登录请求的优化
  3. IM消息送达保证机制实现(一):保证在线实时消息的可靠投递
  4. IM消息送达保证机制实现(二):保证离线消息的可靠投递
  5. 如何保证IM实时消息的“时序性”与“一致性”?

6、IM群聊思路设计

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

具体架构思路如下所示:

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

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

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

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

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

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

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

  1. IM单聊和群聊中的在线状态同步应该用“推”还是“拉”?
  2. IM群聊消息如此复杂,如何保证不丢不重?
  3. 移动端IM中大规模群消息的推送如何保证效率、实时性?
  4. 现代IM系统中聊天消息的同步和存储方案探讨
  5. 关于IM即时通讯群聊消息的乱序问题讨论
  6. IM群聊消息的已读回执功能该怎么实现?
  7. IM群聊消息究竟是存1份(即扩散读)还是存多份(即扩散写)?
  8. 一套高可用、易伸缩、高并发的IM群聊、单聊架构方案设计实践

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

  1. 网易云信技术分享:IM中的万人群聊技术方案实践总结
  2. 阿里钉钉技术分享:企业级IM王者——钉钉在后端架构上的过人之处
  3. IM群聊消息的已读未读功能在存储空间方面的实现思路探讨
  4. 企业微信的IM架构设计揭秘:消息模型、万人群、已读回执、消息撤回等
  5. 融云IM技术分享:万人群聊消息投递方案的思考和实践
  6. 微信直播聊天室单房间1500万在线的消息架构演进之路

7、本文小结

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

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

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

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

8、相关文章

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

  1. 跟着源码学IM(一):手把手教你用Netty实现心跳机制、断线重连机制
  2. 跟着源码学IM(二):自已开发IM很难?手把手教你撸一个Andriod版IM
  3. 跟着源码学IM(三):基于Netty,从零开发一个IM服务端
  4. 跟着源码学IM(四):拿起键盘就是干,教你徒手开发一套分布式IM系统
  5. 跟着源码学IM(五):正确理解IM长连接、心跳及重连机制,并动手实现
  6. 跟着源码学IM(六):手把手教你用Go快速搭建高性能、可扩展的IM系统
  7. 跟着源码学IM(七):手把手教你用WebSocket打造Web端IM聊天
  8. 跟着源码学IM(八):万字长文,手把手教你用Netty打造IM聊天
  9. 跟着源码学IM(九):基于Netty实现一套分布式IM系统
  10. 跟着源码学IM(十):基于Netty,搭建高性能IM集群(含技术思路+源码)
  11. SpringBoot集成开源IM框架MobileIMSDK,实现即时通讯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

posted @ 2022-07-04 18:38 Jack Jiang 阅读(75) | 评论 (0)编辑 收藏

本文由作者jhon_11分享,有大量修订和改动。

1、引言

如何设计一款高性能、高并发、高可用的im综合消息平台是很多公司发展过程中会碰到且必须要解决的问题。比如一家公司内部的通讯系统、各个互联网平台的客服咨询系统,都是离不开一款好用且维护的方便im综合消息系统。

那么,我们应该怎么样来设计一款三高特性的im系统,并能同时支持各个业务线的接入(比如:内部OA通讯、客服咨询、消息推送等等功能)有呢?

下面就由我来介绍一下我所负责的公司IM综合消息系统所经历的架构设计历程,以及架构设计过程中的一些思路和总结,希望能给你带来启发。

学习交流:

本文已同步发布于:http://www.52im.net/thread-3954-1-1.html

2、初版IM架构

2.1 概述

im第一版设计的初衷是公司需要一款im消息中间件用于支撑客服咨询业务。

但是,考虑到为了方便日后其他业务线也能接入消息沟通平台,所以一开始就将整个消息中心的能力需求给到中间件团队进行开发,以便除客服外的各业务线接入综合消息中心,从而实现多元的消息实时触达能力。

2.2 初版架构介绍

初版架构图如下图所示:

针对上面的架构图,我们逐个解释一下各模块的作用。

1)存储端:

在初版的架构下,存储端我们使用tidb、redis作为主要存储:

  • [1] redis用于存储消息已读未读,缓存连接信息等功能;
  • [2] tidb作为开源的分布式数据库,选择它是为了方便消息的存储。

2)mq消息总线:

我们使用rocketmq来实现消息总线(PS:即分布式情况下,不同im实例间通过MQ进行消息交互)。

消息总线是整个im的核心,使用rocketmq能支持十万级别的tps。基本所有服务都要从消息总线中消费消息进行业务处理。

3)zookeeper注册中心:各个服务会注册到zk中,方便服务之间内部进行调用,同样也可以暴露服务给外部进行调用。

4)link服务:

link服务主要用于接收客户端的ws(WebSocket协议)、tcp、udp等协议的连接。

同时调用用户服务进行认证,并投递连接成功的消息给位置服务进行消费,存储连接信息。

ws(WebSocket协议)过来的消息先到link再投递到消息总线。

5)消息分发服务:

消息分发服务主要用于接收消息总线推过来的消息进行处理,按照im内部消息协议构造好消息体后,又推送到消息总线中(比如会推给会话服务、消息盒子、link服务)。

6)位置服务:

存储link的(WebSocket协议)连接、tcp连接等信息,并使用redis进行缓存(key为userId),方便根据UserId查询到该用户所登录的客户端连接在哪个link上。

一个用户在相同设备只能登录一个,但可以支持多端登录。

7)用户服务:用于存储所有用户,提供认证查询接口。

8)消息盒子:存储所有消息,提供消息查询、消息已读未读、消息未读数、消息检索等功能。

9)会话服务:管理会话、群聊会话、单聊会话等功能。

2.3 整体时序图

整体架构的时序图如下:

3、初版IM架构存在的问题及思考

在上节的架构设计介绍中,我们详细分享了初版IM系统架构的设计思路以及具体流程。

那么在初版IM架构设计中还存在什么样的问题,又该如何优化呢?我们一条条来看看。

3.1 使用MQ消息总线的问题

正如上节所分享的那样,我们初版IM架构中,link服务到消息分发服务的消息使用的MQ消息总线。

初版架构设计中,link服务将消息下推给消息分发服务进行处理时,使用的是mq消息总线(通俗了说,IM集群内不同IM实例间的通信是依赖于MQ进行的消息传递),而mq消息总线必然做对有一定的时延(而且时延受制于MQ本身的系统实现和技术策略)。

举个例子:

当两个处于不同IM实例的客户端A和B聊天时,A用户发送消息到link --> 消息总线 --> 消息分发服务 --> 消息总线 --> link --> B用户。

正如上面这个例子,im消息投递流程太长了,并且这样也会大大降低系统的吞吐量。

3.2 消息落库为写扩散的问题

其实现阶段我们使用的是跟微信一样的写扩散策略(详见《企业微信的IM架构设计揭秘:消息模型、万人群、已读回执、消息撤回等》)。

那么为啥微信使用写扩散不是缺陷,而对于我们的IM架构来说确是缺陷呢?

微信的技术特性:

  • 1)微信号称没有存储用户的聊天记录,全是实时推送;
  • 2)微信聊天记录全部会在我们手机端存储一份,两台手机终端上的聊天记录并不互通,并且互不可见。

我们的IM综合消息中心技术特性:

  • 1)综合消息中心是会有拉取历史聊天记录(服务端拉取)的功能,存储了全量消息;
  • 2)综合消息中心的客户端,需要支持网页版本。

综上所述:

  • 1)写扩散对微信这样有移动端的富客户端版本的即时通讯产品十分友好,每个消息在消息分发的时候给处于这个会话(单聊,群聊)下的所有用户所在客户端先推送消息,没找到连接就针对这个用户写一个离线缓存消息,那么下次该用户登录进来,可以从缓存中拉取到该消息,并且清掉缓存;
  • 2)写扩散对于我们这类通用综合消息平台并不友好,由于接入方大部分是网页版的客户端,所以没有缓存消息的能力,浏览器刷新就没有了任何消息,所以需要实时去服务端拉取历史消息。假设我是写扩散,在一个群聊中有五百个用户,针对这五百个用户在这个会话,我需要去写五百条消息,大大的增加了写io,并且还不能写缓存(得写数据库)。

3.3 tidb存在不稳定性和事务并发的问题

tidb是目前主流的开源分布式数据库,查询效率高、无需分库分表。

但同样的,tidb存在一些隐藏的问题:

  • 1)tidb在高并发情况下,并发事务会导致事务失败,具体原因不知;
  • 2)tidb排错成本高,公司很少有tidb专业运维,经常遇到不走索引的情况。

3.4 群聊、单聊冗余在同一个服务的问题

在我们初版的IM架构设计中,单聊和群聊是冗余在会话服务中的,并且冗余在同一张表的。

其实单聊、群聊从数据角度来说,还是会有些不同(比如业务属性)虽然都是会话,我们还是需要将这两个服务拆分开,细粒度的服务拆分能更好的把控整体的逻辑。

4、升级版IM架构

4.1 初始架构问题

正如前面两节分享的那样,渐渐的我们发现初版im架构有很大的不足之处。

在生产上暴露出了以下问题:

  • 1)tps没达到预期,吞吐量不能满足公司业务的发展;
  • 2)使用的存储中间件难以维护(主要是tidb),试错成本高,经常在生产暴露问题,并且速度越来越慢;
  • 3)消息写扩散没有太大必要,并大大增加了系统io次数(原因见上一节);
  • 4)一些特性无法支持,比如消息图文检索,消息已读未读。

4.2 升级版im架构介绍

本次升级后的im架构如下图所示:

如上图所示,改版后的各模块情况如下:

  • 1)存储端:存储端我们改用了mysql,针对消息服务单独使用了主从mysql集群(主节点用于写消息、从节点用于消息检索)——;
  • 2)mq消息总线:与第一版相比没有改动;
  • 3)link服务:与第一版相比,改动了link服务到消息分发服务的消息推送方式(由MQ总线方式变更为tcp实时推送);
  • 4)消息分发服务:集成了消息处理能力、路由能力,每台消息分发服务拥有所有link服务的tcp连接;
  • 5)单聊服务:负责单聊会话的管理能力;
  • 6)群聊服务:负责群聊会话的管理能力;
  • 7)用户服务:提供用户认证,登录\注册能力。

5、详细对比针对初版IM架构的改动

升级版的IM架构,对比初始初始,具体主要是下面这些改动。

5.1 改进了不同im实例间的消息分发方式

针对初版MQ消息总结的问题,升级版架构中,我们将link到消息分发服务改为tcp实时连接,百万客户端连接同一台link机器,消息实时触达能力tps达到16万。

link到消息分发服务的改版是本次设计的亮点之一,完全消除了mq推送的时延性,并且路由简单,几乎实时触达。

举个例子:当两个处于不同IM实例的客户端A和B聊天时

  • 1)初版架构中是:A用户发送消息到link --> 消息总线 --> 消息分发服务 --> 消息总线 --> link --> B用户;
  • 2)升级版架构是:用户A --> link --> 消息分发 --> link --> 用户B。

而且:link服务到消息分发服务集群的消息推送使用轮询负载均衡的方式,保证公平,不会导致个别机器负载过高。

5.2 取消了位置服务

取消了位置服务(这里的位置不是指的IM消息里的地理位置消息哦),消息分发服务集成位置服务的能力。

消息分发服务本身业务简单,不需要再单独划分位置服务,因为会增加网络io,并且消息分发服务直连link,而让它负责路由则更加方便。

5.3 存储由tidb改成了mysql

存储端由tidb改成了mysql,增强了可维护性,消息服务使用mysql主从读写分离方式,提高了消息落库速度与检索速度的同时,也减轻数据库压力。

前面有提到过使用tidb这样维护成本高,排查问题难的分布式数据库是一件很痛苦的事情。

而我们使用mysql更加稳定,大家对mysql的学习成本相对较低。针对消息服务使用读写分离的方式,能大大提高消息的吞吐量。

5.4 实现了初版无法实现的特性功能

升级版架构中,我们实现了初版无法实现的特性功能,比如消息已读未读、红包推送、商品链接推送等功能。

新版综合消息中心加入了消息已读未读、发送红包、链接推送等功能,但这些功能带有一定的业务特性,毕竟不是所有Im都需要,可通过配置取消这些功能。

5.5 消息由写扩散改为读扩散

升级版IM架构中,消息存储由写扩散改为了读扩散。

前面我们有提到写扩散和读扩散的利弊,对于网页端IM我们更适合使用读扩散,只需要落一条消息,大大提高消息服务的吞吐量.

5.6 增加了门面服务

升级版IM架构中,我们增加门面服务 im-logic,用于暴露给第三方业务线接口调用。

初版架构中,都是im的各个服务各自暴露接口给到外部进行调用, 而升级版架中我们统一使用logic服务暴露给外部调用。

在logic服务针对调用可以做一些处理,这样不会影响到整体im的通用,不会增加im底层代码的复杂度,从而将业务逻辑与底层进行解耦。

6、优化后的效果对比

针对升级版和初版IM架构,我们也做了一些对比测试,具体的测试过程就是详细展开了。

以下是测试结果:

7、业务线接入im综合消息系统的业务划分思考

7.1 到底该如何设计高性能通用im综合消息系统

关于业务线接入im综合消息系统的业务划分,我也做了一些总结和思考,为了更形象和易于理解,我这里以客服系统以及企业微信为例来进行分析。

假如我开发了一款通用的im综合消息系统,现在有很多业务方需要接入我们,我们该如何进行业务域的清晰划分就显得尤为重要,需要在妥协与不妥协中进行平衡。

就像当前市面上开源的im消息平台来说,存在的问题主要是:要么是集成了很多的业务逻辑,要么就只是一款单纯的客服系统,再或者就是一款IM好友聊天系统,中间的业务划分并不明确。当然,这也有好处,拿来就能用,并不需要进行二次业务封装。

那么,到底如何将im设计为一款真正的高性能通用im综合消息系统呢?

通用的综合消息消息平台只需要有通用的底层能力:

以下案例假设在我已经按照上述架构设计了一版im综合消息中心。

7.2 以客服系统为例

客服系统:

客服系统不光需要实现自身业务,还需要整合im的消息能力(消费im的消息),来进行场景分析,实现会话变更、信令消息推送等逻辑。

客服系统内部需要根据im的底层支持能力进行相应的业务封装以及客服系统的客服用户池,c端用户池如何初始化到im的用户中心这些问题都是需要考虑进去的。

7.3 内部OA通信为例

内部OA通信:

 

员工内部OA通信系统需要集成IM好友功能,需要根据im的用户中心封装组织架构,用户权限等功能。

同时,内部通信系统需要根据im实现消息已读未读,群聊列表,会话列表拉取等功能。

8、本文小结

im的综合消息平台是一款需要高度结合业务的中间件系统,它直接与业务打交道,跟普通的中间件有根本的区别。

一款好用的im综合消息平台,直接取决于你的通用性,可扩展性以及系统吞吐能力。

希望这篇文章所分享的内容,能对大家开发im时候的思路有所启迪。

9、参考资料

[1] 从零到卓越:京东客服即时通讯系统的技术架构演进历程

[2] 从游击队到正规军(一):马蜂窝旅游网的IM系统架构演进之路

[3] 瓜子IM智能客服系统的数据架构设计(整理自现场演讲,有配套PPT)

[4] 阿里钉钉技术分享:企业级IM王者——钉钉在后端架构上的过人之处

[5] 新手入门一篇就够:从零开发移动端IM

[6] 零基础IM开发入门(一):什么是IM系统?

[7] 基于实践:一套百万消息量小规模IM系统技术要点总结

[8] 一套亿级用户的IM架构技术干货(上篇):整体架构、服务拆分等

[9] 一套亿级用户的IM架构技术干货(下篇):可靠性、有序性、弱网优化等

[10] 从新手到专家:如何设计一套亿级消息量的分布式IM系统

[11] 企业微信的IM架构设计揭秘:消息模型、万人群、已读回执、消息撤回等

[12] 阿里IM技术分享(三):闲鱼亿级IM消息系统的架构演进之路

[13] 一套高可用、易伸缩、高并发的IM群聊、单聊架构方案设计实践

本文已同步发布于:http://www.52im.net/thread-3954-1-1.html

posted @ 2022-06-28 10:40 Jack Jiang 阅读(93) | 评论 (0)编辑 收藏

关于MobileIMSDK

MobileIMSDK 是一套专门为移动端开发的开源IM即时通讯框架,超轻量级、高度提炼,一套API优雅支持UDP 、TCP 、WebSocket 三种协议,支持iOS、Android、H5、标准Java平台,服务端基于Netty编写。

工程开源地址是:

关于RainbowChat

► 详细产品介绍:http://www.52im.net/thread-19-1-1.html
► 版本更新记录:http://www.52im.net/thread-1217-1-1.html
► 全部运行截图:Android端iOS端
► 在线体验下载:专业版(TCP协议)专业版(UDP协议)      (关于 iOS 端,请:点此查看

 

RainbowChat是一套基于开源IM聊天框架 MobileIMSDK 的产品级移动端IM系统。RainbowChat源于真实运营的产品,解决了大量的屏幕适配、细节优化、机器兼容问题(可自行下载体验:专业版下载安装)。

* RainbowChat可能是市面上提供im即时通讯聊天源码的,唯一一款同时支持TCP、UDP两种通信协议的IM产品(通信层基于开源IM聊天框架  MobileIMSDK 实现)。

v8.2 版更新内容

此版更新内容更多历史更新日志):

(1)Android端主要更新内容新增“扫一扫”等功能及优化!】:

  • 1)[bug]解决了客户端被踢掉后,再次登陆时提示socket错误的问题;
  • 2)[优化]优化了扫码加群界面中,群头像加载失败时的默认显示样式;
  • 3)[优化]优化了切换账号和被踢时跳转到登陆界面的切换性能;
  • 4)[优化]重构了主要类代码,更方便集成;
  • 5)[新增]搜索功能(支持好友、群聊、聊天记录搜索(与微信逻辑一样));
  • 6)[新增]“聊信信息”界面中新增“查找聊天记录”功能;
  • 7)[新增]“群聊信息”界面中新增“查找聊天记录”、“清空聊天记录”功能。

(2)服务端主要更新内容:

  • 1)[优化][服务端]升级了MobileIMSDK至v6.2beta(改动了onUserLoginout方法参数);
  • 2)[优化][服务端]解决了log4j2的两个jar包冲突导致在linux下不能正常输出log的问题.

此版主要新增功能运行截图更多截图点此查看):

posted @ 2022-06-25 22:37 Jack Jiang 阅读(68) | 评论 (0)编辑 收藏

     摘要: 本文由字节跳动技术团队开发工程师王浩分享,即时通讯网收录时有较多修订。1、引言对于移动互联网时代的用户来说,短视频应用再也不是看看视频就完事,尤其抖音这种头部应用,已经是除了传统IM即时通讯软件以外的新型社交产品了。对于中国人一年一度最重的节日——春节来说,红包是必不可少的节日特定社交元素,而抖音自然不会被错过。在2022年的春节活动期间,抖音将视频和春节红包相结合,用户可...  阅读全文

posted @ 2022-06-20 17:12 Jack Jiang 阅读(86) | 评论 (0)编辑 收藏

Jack Jiang的 Mail: jb2011@163.com, 联系QQ: 413980957, 微信: hellojackjiang