Jack Jiang

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

2024年3月28日

本文由云音乐技术团队燕十三分享,有修订和改动。

1、引言

本文分享的是网易云音乐技术团队基于实时消息总线技术,解决了直播活动系统的模块灵活组合、消息治理与异步履约等问题,希望能给你带来启发。

cover-opti

2、系列文章

本文是系列文章中的第 10 篇:

  1. 直播系统聊天技术(一):百万在线的美拍直播弹幕系统的实时推送技术实践之路
  2. 直播系统聊天技术(二):阿里电商IM消息平台,在群聊、直播场景下的技术实践
  3. 直播系统聊天技术(三):微信直播聊天室单房间1500万在线的消息架构演进之路
  4. 直播系统聊天技术(四):百度直播的海量用户实时消息系统架构演进实践
  5. 直播系统聊天技术(五):微信小游戏直播在Android端的跨进程渲染推流实践
  6. 直播系统聊天技术(六):百万人在线的直播间实时聊天消息分发技术实践
  7. 直播系统聊天技术(七):直播间海量聊天消息的架构设计难点实践
  8. 直播系统聊天技术(八):vivo直播系统中IM消息模块的架构实践
  9. 直播系统聊天技术(九):千万级实时直播弹幕的技术实践
  10. 直播系统聊天技术(十):基于实时消息总线的活动系统架构设计》(* 本文

3、技术背景

所谓组装,就离不开老生常谈的复用,我们可以对大部分认为比较共性的场景做好系统级别的封装,封装成一个个复用度较高的服务,然后通过接口和扩展点的方式进行一部分的能力开放,但是有一种场景是解决不了的,就是当一个功能级别的代码执行结束后,希望触发到另外一个功能,同时希望这个功能是可以通过配置去解决的,并且不需要通过开发的手段去解决这类问题。

例如,用户送了一个礼物给一个主播,直播间的贡献榜上对该用户做了积分 +1,这是一个很典型的「履约」类场景,与我们在直播间内下单购买,履约给仓储系统道理是一样的,但是这都建立在这个流程模式是固化的。

而活动往往不是这样的,活动相比这些固化的流程是更为灵活的。

笔者的团队曾经开发过这样一个场景:

用户通过送礼,去帮助主播完成一个直播间虚拟能量条的冲击,而每充满这个能量条都希望做一件事情,这个事情就彰显出业务侧的脑洞大开,第一次的活动是希望给某个榜单加上分数,第二次的活动希望是给主播掉落一个虚拟的宝箱,第三次的活动是给用户发送一些抽奖券,依次类推,只要是做过的功能,他都希望这个事情可以去用,时间久了,就会面临频繁去修改这个模块的代码,当它「结束」后,if 条件式的去触发各个代码,或者策略模式的去魔改代码,对于长期建设来看这种方式并不友好,就我看来这只是这个模块结束后要做这么多事情,那么下一个模块如果从这么多事情挑两件岂不是还要再写一堆代码?

因此我们想到了一个相对比较原始的解决方案:总线式服务。

4、信鸽服务的组合能力

与其说组合,更倾向于用「履约」这个词形容会比较恰当一点,因为对于活动来说,信鸽只做「履约」这一侧的功能比较重要,他要解决的是异步场景的分发类问题,而不是对一些系统做一些 adapter 组合集成的能力。

这里以一个活动场景来做示例:

1

音乐人活动的一个简单的页面主要包含了几个模块功能:

  • 1)用户通过送礼、观看等行为完成相关任务;
  • 2)为喜爱的音乐人加进度条积分;
  • 3)当进度条积分完成某个档位后,触发宝箱的掉落、飘屏送礼等。每个档位的类型是不同的;
  • 4)幸运锦鲤的模块。

对于这样一个活动,从开发的角度来看,其实它是由几个模块进行组装的,除了 4 是需要独立开发的,1、2、3 都可以通过现有的系统进行组装,这里 1 抽象为任务系统,2 抽象为进度条系统,3 也可以抽象为宝箱系统、送礼系统等。

下图是对场景抽象化模块的概念:

2

那么难点来了,通过什么手段可以来将这些模块进行组装呢?

通过图中可以看到,当一个模块完成了他的生命周期,可以发送一份数据,路由系统收到了这个数据之后,会帮我们做一层路由的转发,决定数据会路由到下一个系统上去。下一个系统可能是「奖励系统」也可以是「进度条系统」。

同时可以对这个行为进一步细化的抽象,我们希望这个路由系统充当一个「总线」的角色,「信件」代表了每一个系统希望收到的数据,同时对每一个系统都抽象化成一个目的地,如果我们配置了一份路由关系(抽象为信鸽配置),那么这只「信鸽」可以作为将数据信件为我们带到我们想去的任何目的地,那么对于系统的好处是,系统只需要提供自己接入这个路由系统的能力即可,下一次随便是什么活动,可以直接做一些组合关系。

有了信鸽路由这种思路,针对音乐人活动这种场景,我们可以将一个流程的完整链路梳理出来:

3

图中可以看到,我们对任务、进度条等基础模块,做了一些扩展点(EXT-POINT)来满足业务流程的编排,这在中台里是比较常规的解决问题的手段之一,而在系统模块组组合的场景中,多了一层信鸽服务的概念,在【2】、【3】、【4】的处理流程中,都可以由信鸽来决定数据流向哪个系统,事实证明这个方案是可行也并且有效的。

5、整体架构设计

那么如何设计这样一个组合能力的架构呢?

研发的重心可以分为四个层面:

  • 1)履约能力:当代码结束后做到末端的触发。
  • 2)SDK 接入能力:Interface 级别的包装,天然的 Autoconfigure 能力。
  • 3)全局的标识字典:一份数据如何让所有接入的系统都可以达成共识。
  • 4)系统的自动注册能力:接入 SDK 后,自动上报到信鸽服务的统一管理,自动激活,不需要人工的介入开启。

基于这四个层面,架构设计如下图:

4

从图中可以看到:左侧虚线框代表了一个系统的触发,右边虚线框代表了最终另外一个系统的触达。信鸽系统充当了代理的服务,触发的系统只需要接入信鸽 SDK,当执行完自己的职责之后,对数据做一层组装发送给信鸽系统,信鸽系统会根据发送的信鸽 id,寻找到信鸽的相关配置,信鸽 id 决定了数据会流转到那个接入的子系统上,这个流转可以是同步的也可以是异步的(当然大部分都是异步场景),异步主要依赖「RocketMQ」的投递能力,当转发失败后,会对投递的数据结果集进行备份存储,用于定时步长的重试操作。在以往的实践过程中,大部分场景都是异步的链路,不需要获得下一个子系统提供的返回的结果集,并且「RocketMQ」本身投递消息的出错率也是小概率事件(毕竟4台 broker,出错3台的可能性是极低的),相比 RPC 这类通信级别的接口有绝对的优势。

6、提供的SDK能力

6.1 路由触发

在上图中,我们可以看到对于子系统分成了大概 2 类场景,一种是活动业务域,另外一种是非活动业务域,这本身与业务的场景有关,我们希望所有的子系统都可以按照标准去接入 SDK,但是并不能保证每个子系统提供能力都依靠 SDK,对于一些非活动业务域使用了定制化开发的模式来进行桥接工作,这种桥接工作更像是传统的 adapter 和 ESB 总线思路。

而活动业务域的子系统都可以采取接入 SDK 的模式,这里主要会介绍一下异步的设计思路,当一个子系统接入 SDK 后,会在 Spring 容器创建 bean 的时候,默认创建一个 PushConsumer 的 bean ,添加监听信鸽 「fly」 的 「Listener」 ,这样能做到自动消费到路由的消息,对消息进行解析,假设这个系统可以承担的模块能力分别是 S1 / S2 ..., SN 等功能,那么整体实现图如下图所示。

5

6.2 自动注册

每一个 Provider 的 Server 都是一个独立的应用服务集群,对于每一个 Provider 来说他提供的能力并不是单一的,正如上文所说,一个领域服务(活动 TOY 服务:主要提供各类积木式玩法领域),他能够提供的模块是非常多样的,诸如宝箱、进度条、留言板等等,「act-toy」 服务他具备了许多功能,当 「act-toy」 服务接入 SDK 后,就需要把自己子域需要发布成 Provider 的功能注册到信鸽上,注册的方案如下图。

当一个 Server 拉取 SDK 的启动后,会定时的拉取定义好的 Interface ,对实现的 Class ,获取自定义注解的 Type 类型,通过顺序消息的方式注册到信鸽服务上,采用了启动 + 定时推送的方式,信鸽服务收到相关的注册信息可以后会将其存储。

6

7、与ESB消息总线方案的比较

从整个组合的能力上看,整体的设计思路是符合总线式服务,而总线式服务业内比较经典的,就是 ESB 总线。

ESB 总线这个技术用今天互联网的角度来看是比较过时的,因为他本身更适应用于大型 IT 企业内部的一些跨语言类服务架构方案,而他本身承接了系统和系统之间的桥接能力。但是信鸽本身的设计不同于 ESB 总线,他更注重的特点是「哑管道」的概念,忽略集成的适配转换,也不适合做中心化同步的集成。

那么具体的比较如下:

7

那么可以从2个 ESB 场景来介绍信鸽的优势所在,笔者理解的 ESB 总线技术大致分两类:

1)开源的 ESB 解决方案:这类是提供开源的一种框架技术,部署在同一个 Tomcat 容器中,开发若干个 Bundle 可以运行在容器中,并且能做到热替换的作用,但是每个 bundle 需要通信的模式需要定制私有协议。由于这类开源框架较早,提供的通信协议大部分是 WebService 类的,因此在开发过程中,开发的成本会非常高,例如 Apache 早期开源的 ServiceMix 。关于协议,需要写大量的 「wsdl」 做通信,大多数也是依赖 WebService 发布的接口。

2)自研的 ESB 集成能力:笔者曾经参加过一个企业级的项目,大致是 ESB 的总线集成了各路系统,包括了 Rest 协议接口、C++ 服务发布的 WebService 协议、海外第三方公司发布的 WebService 协议,为了打通公司内部 C++ 服务、Java Web 服务与海外第三方系统,采用的就是做一个集成的总线,这里需要太多的协议转换,包括如何解析 「wsdl」 文件读取节点数据,转换成 Json 等等,这类场景在企业级服务是常见的事情,本身不适用互联网场景,侧重点更偏向于同步接口协议的转换,同时还不知道编排的能力,大部分依靠硬编码来解决。

无论 1 还是 2 与我们信鸽设计思路都是不同的,信鸽本质上强调的是异步去做末端的事件,选用轻量的协议结构,在技术上属于同语言系,因此关于字典的定义也是标准化的,也就是说,userId、anchorId 分别代表了用户id和主播id,并不需要任何一方突发奇想定义 uid、aid 这类的字样,由信鸽来定义统一化的字典,同时编排统一收拢到信鸽服务来确定,是可视化的,不需要编写复杂的 XML 节点文件。

8、与Pipeline流水线方案的比较

从总线这个概念来看,或多或少同「管道」是类似的,Pipeline 的思想被广泛应用在诸多技术领域中。

比如:

  • 1)CI/CD 持续集成的场景;
  • 2)开源框架中的流水线设计模式,例如 Netty框架中的网络字节流处理等;
  • 3)业务自定义的一些工作流流转技术。

第一种场景:更倾向于 DevOps 的解决方案,从持续集成,持续交付,持续部署,为了快速、自动化、可重复的方式的去处理工程,与我们今天要解决的上层业务编排场景是完全不同的两个领域。

第二种场景:本质上是一种代码实现的设计模式,像 Netty 中,采用的是「责任链」的设计模式去实现,网络字节流经过「工厂流水线」后,进行包装,最后得到一个成品,与我们今天要解决的业务同样不是一个领域。

第三种场景:是业务开发过程中经常遇到的问题,尤其是有复杂流程的场景中,这里包含了对流程的编排、服务的编排,每个代码块和服务都可能作为一种处理的「节点」,在整个流水线中进行串排完成业务的实现。与我们的信鸽有什么不同呢?

8

在我看来,两种技术方案是可以同时存在的,我们对已经稳定的领域场景,做一些灵活自定义的一些流程编排,这些流程可以作为「流水线」的思路去实现,在「流水线」末尾的一个流程节点上可以定位为「信鸽」的节点,这个节点可以再继续自由组合定制化的活动场景。

9、信鸽服务的消息转发能力

9.1 面临的问题

直播的活动与营销活动的不同,大部分触发的场景偏向于平台的下游,因此需要监听许多 topic,去实现自己的业务编码,而活动在非成熟的形式背景下,必然要面临大量的短期代码,短期代码的生命周期往往只局限于活动周期内,这类代码代表了探索,代表了拓荒。直播活动在领域建模上是具备双面性的,一方面要在历史的经验去做经验复制和沉淀,另一方面要具备快速展开短期代码的能力。而随着服务数量的增多,就面临着许多服务要监听 topic,而这些 topic 对于 A 服务可能是监听过的,对于 B 服务可能也是监听过的,面临着这类问题,我们需要把 topic 接入的代码做一次代码的 Copy 或者做一些包来解决,但是这并不是一个友好的解决方案。同时,这也面临着另外一个问题,另外一个问题就是当一个代码块完成他的使命后,再也没有开启的那天,他接入的 topic 会一直进行空转的消费,我们不可能经常的对 「Nydus」(云音乐版 RocketMQ )管控平台去做一些消费下线,时间久了,消息治理就变得比较棘手起来,就如同我们去分层解决服务循环依赖,没成想有一天异步链路中也出现了一团乱麻。下面这个图代表了 「dev」 过程中遇到的问题:

9

9.2 解决思路

我们希望信鸽可以继续发挥他的优势,它不仅仅是一个只做业务末端履约的一个总线服务,他更应该帮助我们活动域内的服务做好消息治理这方面的工作,首先需要有一个研发思路的转变,就是开发同学需要为自己 「dev」 的模块去提供他的 topic,这个 topic 与自己 「dev」 的模块是一个技术闭环的,如果每一个 「dev」 的模块都具备这样的能力,信鸽只需要发挥他的优势,对本来需要监听的 topic 变成由信鸽转发给模块的 topic。

10

按照这个解决思路的前提下,我们希望信鸽需要具备的就是消息转发的能力,而这个转发的场景可以抽象为三种。

具体是:

1)业务自定义的分发场景:这个场景是一定要做一些特定业务处理的,通过原始消息,做一些业务清洗工作,再去转发到一些业务场景中,属于定制化的场景。

2)注册上报自动分发的场景:这个场景只要保证信鸽服务的 receiver 监听的 topic 足够,那么信鸽就可以自动分发到各个业务模块的 topic 上,其中分发的消息结构是具备异样的,可以通过 tag 的不同让 consumer 自住选择性拉取,根据 tag 的区分,同时也能解决某类 topic 消息过多,导致饿死的场景,犹如上图提到的一样。

3)消息存储的场景:并不是所有的消息都需要存储,在活动中我们认为较为重要的消息是需要做备份,方便后期去做回放,例如礼物消息,是任何场景使用率较高的,为了提升写入的能力,可以采用批量消费的消费方式,做batch的写入,在写入这里初步选型是用TiDB,TiDB相对DDB(网易分布式数据库)比较适合去存这类消息,同时,这类消息大部分时候是不需要做读操作。

按照提到的三个场景,整体的架构如下:

11

我们的 receiver 可以根据配置过的 topic 动态的在 spring 容器里创建 consumer 的 bean,兼容了新接入 topic,需要修改信鸽服务代码的问题,同时在收到了 topic 消息的那一刻,消息转发如图中所描述一样,分成了三条路。

这三条路代表了:自动转发、自定义转发、消息存储,这里选用三个不同的 consumer,保证消费线程池的足够宽,和转发 topic 队列足够可消费,自动转发收到消息后会根据源 topic 的类型再一次做 Tag 区分的转发到配置的 topic 关系上,同时这个转发关系是可以让研发自助管理的,也可以配置他的存活周期,非常适用于活动短期场景,在活动结束后,减少了服务的空转消费的情况。当然毕竟也要考虑到另外一个问题,多了一跳的操作会导致到转发的失败,对于这种场景,我们会对失败的消息做 exception 的存储,进行重试处理。

同时:我们对信鸽的转发能力做了压力测试,队列的长度设置的足够宽,不考虑写表链路的场景下,单纯的转发能力,消耗 io 的点主要还是集中在 broker 通信的场景上,如果考虑消息都采用异步落盘的情况下,系统的吞吐量会更优,选用了 「8U16G」 的服务器配置,在 32 台 docker 云容器的支撑下,receiver 是可以承担到 300w/min 的消息量,并且 cpu 还能保持到 45% 左右。

虽然信鸽的转发能力解决了我们的问题,但并不代表这是个最优解,我希望的最优解是可以让信鸽搭载 FaaS 平台,毕竟 FaaS 可以提供很多关于消息清洗的场景,而且 FaaS 在机器资源调度上会有更好表现。FaaS + BaaS 这样的组合,是未来系统技术转型的趋势。

10、本文小结

花费时间做一个系统到底带来什么样的好处,又遇到什么难点呢?

10.1 开发维护性

这里用一个 case 来描述研发在使用信鸽服务做业务开发后会带来怎样的好处,大勇作为一名业务研发同学,今天他需要开发一个活动,活动涉及到榜单晋级、直播间杀怪、直播间飘屏、主播任务。

这个活动的流程思路是:

  • 1)主播完成了 「xx」 任务后在直播间掉落一个「怪物」,并发送飘屏通知。
  • 2)主播完成了 「xx」 任务后给榜单加上一些分数。
  • 3)榜单跨日晋级 topN,topN 发送飘屏通知。

很幸运的是、以上涉及到的功能,以前都开发过了,这一次只需要完成组合,很不幸的是,大勇需要开发这类组合能力。直到他遇到了信鸽,一切迎刃而解了,每个功能都接入了信鸽,那么大勇只需要通过信鸽后台,配置好任务完成要飞向「杀怪」、「飘屏」等等信鸽,对于大勇可能只需要很短的时间提测了,发布流程也减少了,一次性的胶水组装代码也不需要,毕竟完成任务掉落怪物,这个逻辑写在任务系统好像也不是很合适。

10.2 遇到的困难

在做消息管理这类问题处理的时候,实现业务覆盖的时候遇到了很多难点。

我们要对现有系统已经接入的 topic 做一些改造,在新业务场景中是屡试不爽的,但是在旧系统中(例如任务系统),新提供了一个任务的 topic,收发路由的旧消息,虽然我们也按照 SDK 的方式做了根据 tag(源 topic)做一些不同 service 的区分,但是这里依然避免不了关于数据清洗和数据结构的协议转换问题,这类问题可能与任务系统本身的清洗思路是有问题的,而这类问题最佳的解决方案一般可以选用脚本语言去做消息清洗会更加灵活一点。

同时在做容量评估的时候,初期的压测并不是很顺利,由于写表链路与信鸽服务存在一个服务中进行压测,如果对某些消息进行数据写入 TiDB 的话,即便是批量消费写入整体服务的吞吐量也是难以压上去的,因此对写TiDB链路的服务单独进行独立,单独进行这方面的压测,信鸽原始服务只做转发,而写存储这一块可以单独去做写能力的评估。

去掉了写 TiDB 链路的场景,单独对服务的进行吞吐量压测,起初选用的 「4U8G」 的服务器资源,由于整体的转发性能较为吃 cpu ,当提升了规格之后,整体的吞吐量有了很明显翻倍,而介于线上消息量的评估,100w/min 的消息可能是我们现有业务的极限状况,我们分别按照不同消息量做了压测,最后输出了一个压测结果集,会根据不同的消息量区间做适当的扩容和缩减。

10.3 未来展望

本文就云音乐大直播活动中台技术团队在日常研发过程中遇到关于业务场景组合、消息管理这类问题,提供了一种系统设计的思路,希望可以帮助读者在日常开发提供一些参考的意见。

目前主要还是围绕着解决现有技术侧问题所展开的,后期会考虑对消息回放这一块作为修复数据的一个重要手段和解决方案,站在异常处理的视角上,如何帮助研发同学快速修复线上问题。

同时,面对未来国际化的场景下,对于消息用户地区机房不够敏感的场景下,希望可以通过一些手段来帮助业务侧消息转发到相关机房,协助 「Nydus」国际化后,解决业务侧路由的不清晰之处,而在未来的国际化路由机房的基础上,如果做到模块之间消息可以准确的路由到用户所在机房也是我们需要更加深入思考的问题。

我们希望信鸽可以作为直播相关产品在活动业务域的重要解决手段之一,帮助更多相关同学解决「复用」、「组合」的烦恼,同时希望它可以国际化,适应更多的产品场景之中。

11、参考资料

[0] 移动端实时音视频直播技术详解(一):开篇

[1] 海量实时消息的视频直播系统架构演进之路(视频+PPT)

[2] 百万在线的美拍直播弹幕系统的实时推送技术实践之路

[3] 阿里电商IM消息平台,在群聊、直播场景下的技术实践

[4] 微信直播聊天室单房间1500万在线的消息架构演进之路

[5] 百度直播的海量用户实时消息系统架构演进实践

[6] 百万人在线的直播间实时聊天消息分发技术实践

[7] 直播间海量聊天消息的架构设计难点实践

[8] vivo直播系统中IM消息模块的架构实践

[9] 万人群聊消息投递方案的思考和实践

[10] 海量实时消息的视频直播系统架构演进之路(视频+PPT)[附件下载]

[11] 社交场景下的统一即时通讯im消息流交互层模块化技术实践

[12] 阿里IM技术分享(八):深度解密钉钉即时消息服务DTIM的技术设计

[13] B站IM消息系统的新架构升级实践

[14] 视频直播技术干货(九):千万级直播系统后端架构设计的方方面面

[15] 视频直播技术干货(十):一文读懂主流视频直播系统的推拉流架构、传输协议等

[16] 视频直播技术干货(十三):B站实时视频直播技术实践和音视频知识入门

即时通讯技术学习:

- 移动端IM开发入门文章:《新手入门一篇就够:从零开发移动端IM

- 开源IM框架源码:https://github.com/JackJiang2011/MobileIMSDK备用地址点此

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

posted @ 2026-06-08 11:52 Jack Jiang 阅读(26) | 评论 (0)编辑 收藏

一、基本介绍

1

MobileIMSDK-Web是一套纯JS编写的Web端IM即时通讯框架(含服务端):

  • 1)超轻量级、极少依赖;
  • 2)纯JS编写、高度提炼,简单易用;
  • 3)基于著名的socket.io网络库实现,浏览器兼容性好、服务端并发性能好;
  • 4)支持运行于iOS、Android等移动端浏览器和各种PC端浏览器;
  • 5)能与MobileIMSDK的APP版(原生移动端代码编写)完美互通;
  • 6)可应用于手机端/PC端的网页聊天应用、企业OA、Web端消息推送等场景。

☞ 补充说明:MobileIMSDK-Web是 MobileIMSDK 的姊妹工程,MobileIMSDK-Web专注于Web端网页聊天(或推送),而MobileIMSDK用于原生代码编写的移动端IM(或推送)应用,但二者可完美互通——从而实现原生代码编写的移动端与基于html的网页聊天完美互通。

☞ 关于为何使用的是Socket.io而不是Netty作为MobileIMSDK-Web的网络层,详见:MobileIMSDK-Web的网络层框架为何使用的是Socket.io而不是Netty?》。

二、与MobileIMSDK的区别

2

☞ 关于MobileIMSDK

MobileIMSDK主要使用原生代码编写,应用于非Web网页方式的移动端即时通讯场景下(当然最新的MobileIMSDK框架也支持基于HTML5的WebSocket客户端)。

⭐️ 同步开源地址:

☞ 关于MobileIMSDK-Web:
MobileIMSDK-Web完全使用JavaScript编写,主要应用于不支持HTML5的需要兼容旧式浏览器的Web网页方式的即时通讯场景下(包括但不限于手机端、PC端的网页聊天(或消息推送)等)。

☞ MobileIMSDK与MobileIMSDK-Web的互通:

基于MobileIMSDK-Web开发的开发的网页聊天等和基于MobileIMSDK开发的移动端IM等可以无缝地进行消息互通,两个框架之间的通信协议完全兼容,从而实现您的网页聊天(或推送)与手机端原生代码开发的IM(或推送)进行完美协作,实现多端通信。

☞ 我该如何选择?

选择一:如果您的应用是用原生代码编写,如移动端是原生代码编写或者不需要兼容旧式浏览器,那么您可以将 MobileIMSDK 引入到您的项目中从而实现IM(或推送)应用;

选择二:如果您的应用是基于Web网页且需要兼容旧式浏览器,那么您的最佳选择就是使用MobileIMSDK-Web来开发您的网页端聊天(或消息推送)。

三、设计目标 

原生的WebSocket代码或者原始的socket.io代码,使得网络通信代码与大量前端UI界面代码混在一起,使得UI界面的重构、维护、改版都非常困难。而MobileIMSDK-Web工程将让开发者专注于UI应用层的开发,网络通信层专业的代码交由SDK开发人员,从而解偶Web端IM的UI前端和通信层的耦合性,同时大大降低复杂性。

总结一下,MobileIMSDK-Web的设计目标是为您的Web端IM带来以下便利:

  • 1)前端UI代码与网络通信代码解耦:UI界面的重构、维护、改版都非常容易和优雅;
  • 2)服务端网络通信代码与业务代码解耦:使得服务端的业务逻辑实现起来清晰简单;
  • 3)浏览器端的高兼容性:受益于socket.io框架,MobileIMSDK-Web在不支持WebSocket的旧式浏览器上仍可很好地工作;
  • 4)服务端的高并发、高性能:得益于Nodejs的异步编程模型和高并发特性,基于MobileIMSDK-Web编写的IM服务端拥有极高的并发处理性能。

四、框架组成 

整套MobileIMSDK-Web框架由以下2部分组成:

  • 1)浏览器端SDK:用于开发浏览器端页面,纯JS编写,极少依赖,方便对接基于原生JS、Angular、EmberJS、VUE等各种前端框架;
  • 2)服务器端SDK:用于开发Web端IM的服务端,支持高性能和高并发。

目录+精编源码

五、技术亮点

  • ✅️ 轻量易使用:超轻量级——纯JS编写且极少依赖,高度提炼——简单易用;
  • ✅️ 兼容性好:基于socket.io网络框架,浏览器兼容性好,在不支持WebSocket的旧式浏览器上仍可很好地工作;
  • ✅️ QoS机制:完善的消息送达保证机制(真正ACK应答机制),确保不漏过每一条消息;
  • ✅️ 断网恢复能力:拥有网络状况自动检测、断网自动治愈的能力;
  • ✅️ 支持多种设备:支持运行于iOS、Android等移动端浏览器和各种PC端浏览器;
  • ✅️ 封装的通信协议:实现了一个对上层透明的即时通讯通信协议模型;
  • ✅️ 身份认证机制:实现了简单合理的身份认证机制(socket.io官方并未实现之,资料也几乎没有);
  • ✅️ 全消息路径:实现了client to server、server to client、client to client 共3种消息路径(socket.io官方只演示了广播消息,一对一发送无资料);
  • ✅️ 服务端慢io解偶:开发者可通过使用MQ进行DB等慢io的读、写解偶,保证IM实时消息高吞吐和性能;
  • ✅️ 服务端代码解偶:实现了上层应用代码与sdk核心代码的解偶,上线、下线、c2s消息、c2c消息、身份认证等的回调通知;
  • ✅️ 实现了在线列表:服务端实现了一个高性能的在线用户列表机制;
  • ✅️ 完善的log记录:服务端接入了log4js日志框架,确保MobileIMSDK-Web中的每一个关键步骤都有日志输出,让您的运行调试更为便利;
  • ✅️ 浏览器端代码解耦:实现了UI前端代码与sdk网络通信代码解偶,防止前端代码跟IM核心代码混在一起,不利于持续升级、重用和维护;
  • ✅️ 轻松开启数据加密:一个参数即可开启SSL/TLS通信加密;
  • ✅️ 聊天协议兼容:实现了与MobileIMSDK 完全兼容的协议模型。

MobileIMSDK-Web的浏览器兼容性:

x1

MobileIMSDK-Web的兼容性由socket.io网络框架决定:点此查看兼容性说明

六、性能负载

得益于socket.io网络框架的高性能和Nodejs的异步编程模型,MobileIMSDK-Web可支持单机数万甚至上十万并发连接。当然,每种应用场景都有各自的特点和差异,请视具体场景具体评估之,性能数据仅供参考。(关于为何使用的是Socket.io而不是Netty作为MobileIMSDK-Web的网络层,详见《MobileIMSDK-Web的网络层框架为何使用的是Socket.io而不是Netty?》)

☞ socket.io性能测试讨论:socket.io 高并发实战socket.io保持6万+连接测试?如何实现单服务器300万个长连接的?

七、开发者手册

缩略清单-无阴影

八、Demo运行截图

运行效果all_in_one拼合图-opti

九、产品案例

以下为基于MobileIMSDK-Web的Web端IM产品案例 RainbowChat-Web 的产品情况。

rbpw_tb_main1

下图为RainbowChat-Web的主界面更多截图更多演示视频):

main1

下图为RainbowChat-Web的主界面[聊天窗全屏时]更多截图更多演示视频):

main2

本文内容引用自:http://www.52im.net/thread-959-1-1.html

posted @ 2026-06-01 10:51 Jack Jiang 阅读(28) | 评论 (0)编辑 收藏

本文由ArchSynapse AI分享,有修订和重新排版。

1、引言

在为大型语言模型(LLM)应用构建实时前后端通信系统时,选择正确的底层技术至关重要。

本章节将深入剖析三种主流技术的核心原理:

  • 1)Server-Sent Events (SSE):它作为服务器主导的单向数据流的黄金标准;
  • 2)WebSocket:作为通用的全双工通信解决方案;
  • 3)WebRTC:专注于媒体流和点对点数据交换。

理解它们的技术设计哲学和技术细节,是做出明智技术选型的关键基础。

本文将为读者剖析 SSE、WebSocket、WebRTC 的技术原理,并对比三者在性能、安全与架构方面的优劣势,详解了AI大模型(LLM)在实时通信协议方面的综合技术考量以及最终选择。

cover-opti2

2、AI大模型实时通信技术专题

技术专题系列文章目录如下,本文是第 5 篇:

3、SSE是什么?

Server-Sent Events (SSE) 是一种基于标准 HTTP 协议的技术,专为服务器向客户端推送单向事件流而设计。

1

其工作流程始于客户端创建一个 EventSource 对象,该对象会自动发起一个持久的 HTTP GET 请求。服务器接收到请求后,不立即关闭连接,而是保持响应打开,并以特定格式发送数据。

这个格式要求服务器设置 Content-Type 头部为 text/event-stream

每个事件由一系列字段组成,包括:

  • 1)必需的 data: 字段用于承载消息内容;
  • 2)可选的 event: 字段用于定义事件类型;
  • 3)id: 字段用于标识事件以便在连接中断后恢复;
  • 4)以及 retry: 字段用于指定重新连接前的等待时间。

消息之间必须以两个换行符 \n\n 分隔。

这种机制使得服务器能够在一个长连接上持续不断地推送增量数据,非常适合 LLM 流式传输等场景。SSE 的一大优势在于其天然地融入了现有的 HTTP 生态,能够无缝通过反向代理、负载均衡器和防火墙,无需特殊配置。

☞ 进一步学习:

4、WebSocket是什么?

WebSocket 则提供了一种更为强大和灵活的全双工通信协议。它通过一次性的 HTTP 握手来“升级”一个标准的 TCP 连接。

2

握手过程涉及客户端发送一个包含 Upgrade: websocket 和 Connection: Upgrade 等特定头部的 HTTP 请求。如果服务器支持 WebSocket,它会返回一个状态码为 101 (Switching Protocols) 的响应,从而完成协议切换。

一旦连接建立,客户端和服务器就可以随时独立地向对方发送消息,而无需像 HTTP 那样每次都发起新的请求。

WebSocket 消息被封装在轻量级的帧中进行传输,支持文本和二进制数据,极大地降低了网络开销。

这种持久且双向的特性使其成为聊天应用、在线游戏和实时协作工具的理想选择。然而,其复杂性也远超 SSE,因为它是一个独立的协议栈,需要专门的服务器库和客户端处理逻辑。

☞ 进一步学习:

5、WebRTC是什么?

WebRTC (Web Real-Time Communication) 是一个更为复杂的生态系统,旨在实现浏览器间的直接音视频通话和任意数据交换。

3

它的核心思想是尽可能地建立点对点(Peer-to-Peer, P2P)连接,从而绕过中间服务器传输大量媒体数据,以达到最低的延迟。

WebRTC 的整个通信过程分为三个阶段:

  • 1)首先是通过信令服务器交换初始信息;
  • 2)其次是通过 ICE (Interactive Connectivity Establishment) 框架进行 NAT 和防火墙穿越;
  • 3)最后才是建立 P2P 数据通道。

ICE 框架依赖于 STUN (Session Traversal Utilities for NAT) 服务器来帮助设备发现自己的公网 IP 地址,如果直接连接失败,则会降级到 TURN (Traversal Using Relays around NAT) 服务器进行中继。

TURN 服务器虽然能保证连接成功,但会消耗大量带宽,因为所有流量都需要经过它转发。

WebRTC 的另一个关键组件是 RTCDataChannel API,它允许在两个浏览器之间建立一个类似 WebSocket 的 P2P 数据通道,用于传输非媒体数据。

由于其架构的复杂性,WebRTC 通常需要一个额外的信令层(常由 WebSocket 或 SSE 实现)来协调连接的建立。

4

 

☞ 进一步学习:

  1. 访谈WebRTC标准之父:WebRTC的过去、现在和未来
  2. WebRTC实时音视频技术的整体架构介绍
  3. 新手入门:到底什么是WebRTC服务器,以及它是如何联接通话的?
  4. WebRTC实时音视频技术基础:基本架构和协议栈
  5. 实时音视频入门学习:开源工程WebRTC的技术原理和使用浅析
  6. 零基础快速入门WebRTC:基本概念、关键技术、与WebSocket的区别等

6、兼容性、性能与开发复杂度对比

在为 LLM 应用选择实时通信技术时,必须在兼容性、性能和开发复杂度这三个关键维度上进行权衡。本章节将对 Server-Sent Events (SSE)、WebSocket 和 WebRTC 进行全面的横向对比,以揭示它们在不同方面的优劣,从而为技术选型提供数据驱动的依据。

6.1 兼容性与平台支持

首先,在兼容性与平台支持方面,SSE 和 WebSocket 拥有非常广泛的浏览器支持,几乎覆盖了所有现代浏览器,包括 Chrome、Firefox、Safari 和 Edge。Opera Mini 是少数不支持 WebSocket 和 SSE 的例外。IE 浏览器则完全不支持这两种技术。

相比之下,WebRTC 的支持范围稍窄一些,IE 不支持,旧版的 Edge 也不支持,但同样广泛应用于其他现代浏览器及其移动端版本。

对于 LLM 应用而言,这意味着 SSE 和 WebSocket 能够触及更广泛的用户群体。然而,值得注意的是,随着 HTTPS 成为网页标配,HTTP/2 得到了普及,这彻底解决了过去 SSE 在 HTTP/1.1 下因浏览器并发连接数限制(通常是6个)而导致的性能瓶颈,使得 SSE 在现代 Web 架构中表现得更加稳健和高效。

6.2 延迟性能

其次,延迟性能是衡量实时通信技术优劣的核心指标。

理论上,WebSocket 具有最低的延迟,因为它在握手完成后就建立了一个纯粹的、无头开销的数据通道。WebRTC 在 P2P 模式下可以实现极低的延迟,因为它避免了服务器中转。

然而,WebRTC 的延迟受到 NAT/防火墙穿越过程的影响,可能需要 fallback 到较慢的 TURN 中继服务器。

SSE 的延迟取决于底层的 HTTP 版本。在 HTTP/1.1 下,由于队头阻塞(Head-of-Line Blocking),多个 SSE 连接可能会相互阻塞。但在 HTTP/2 下,得益于其强大的多路复用能力,SSE 的性能得到了极大提升,可以与常规 HTTP 请求并行,延迟接近 WebSocket。

一项针对真实世界数据流的测试表明,SSE 和 WebSocket 在 EPS(每秒事件数)方面表现出相当的性能,WebSocket 在高并发下略占优势,但差异在实践中往往可以忽略不计。

因此,对于大多数 LLM 文本流式传输场景,SSE 在 HTTP/2 下已经提供了足够低的延迟。

6.3 开发复杂度

最后,开发复杂度是决定项目成本和上线速度的关键因素。

在这三者中,SSE 的开发复杂度最低。它基于标准的 HTTP,开发者可以利用现有的 Web 服务器和工具链,客户端代码也极为简洁,只需几行 JavaScript 即可启动 EventSource 并监听事件。

WebSocket 的复杂度显著更高。除了协议本身,开发者还需要处理连接管理、状态同步、手动实现心跳包和自动重连逻辑、应对粘性会话(Sticky Sessions)带来的负载均衡挑战,以及在分布式系统中使用 Redis Pub/Sub 等消息队列来广播消息。

WebRTC 的开发复杂度最高,它不仅包含了上述 WebSocket 的所有挑战,还引入了信令、ICE 候选者收集、STUN/TURN 服务器的配置和维护等一系列全新的难题。此外,WebRTC 的安全模型也更为复杂,需要同时保护信令通道和媒体通道。

5

综上所述:SSE 在兼容性、性能和开发复杂度之间取得了最佳平衡,尤其是在 HTTP/2 得到普及的今天。WebSocket 提供了更高的灵活性和更低的理论延迟,但代价是巨大的开发和运维复杂性。WebRTC 则因其固有的复杂性,仅适用于特定的 P2P 和媒体流场景。

7、安全性考量与风险缓解策略

在 LLM 应用中采用实时通信技术时,安全性是不可忽视的核心要素。本章节将深入探讨 Server-Sent Events (SSE)、WebSocket 和 WebRTC 在安全性方面的关键考量、潜在风险及相应的缓解策略。

7.1 Server-Sent Events

对于 Server-Sent Events (SSE),其安全性很大程度上继承自标准的 HTTP 安全模型。

首要的安全措施是强制使用 HTTPS,因为 SSE 的长期连接窗口使其更容易受到中间人攻击(Man-in-the-Middle),窃听或篡改流中的数据。

身份验证是另一个关键环节。由于 EventSource API 不支持自定义 HTTP 头部,传统的 JWT 令牌传递变得困难。常见的解决方案是通过 URL 查询参数传递短时效的访问令牌,但这存在安全隐患,因为令牌可能被记录在服务器日志或浏览器历史中。

另一种更安全的方式是利用带有 SameSite=Strict 属性的 Cookie 来进行认证,这种方式能自动随请求发送,但需要注意 CSRF(跨站请求伪造)攻击的风险,可以通过检查 Origin 头部来缓解。

此外,SSE 端点容易受到 DoS(拒绝服务)攻击,攻击者可以通过建立大量连接耗尽服务器资源。对此,应实施严格的速率限制,限制来自同一 IP 或用户的并发连接数。

7.2 WebSocket

WebSocket 的安全性挑战更为复杂。

虽然 WSS (WebSocket Secure) 使用 TLS 加密来保护数据传输,但其状态化的性质带来了新的攻击面。

首先,WebSocket 缺乏标准化的身份验证机制,开发者必须自行实现,例如在握手阶段通过 Cookie 或查询参数传递凭证。如果未正确验证 Origin 头部,应用将面临 Cross-Site WebSocket Hijacking (CSWSH) 攻击,恶意网站可以在未经授权的情况下建立 WebSocket 连接并访问敏感数据。

其次,WebSocket 容易受到 DoS 攻击,特别是通过 Flood 攻击(发送大量小消息或建立海量连接)来耗尽服务器 CPU 或内存。有效的防御措施包括要求连接前进行认证、限制消息大小和频率、以及实施精细的速率控制 [89]。输入数据也可能导致注入攻击,如 SQL 注入或 XSS,因此所有传入的消息都必须经过严格的验证和清理。

最后,由于 WebSocket 连接是持久的,如果令牌在连接期间过期,可能会导致会话劫持,因此需要实现 token 刷新机制。

7.3 WebRTC

WebRTC 的安全性设计更为严格,其核心理念是端到端加密。

所有媒体流都默认使用 SRTP (Secure Real-time Transport Protocol) 加密,而数据通道则使用 DTLS (Datagram Transport Layer Security) 加密,确保只有通信双方可以解密数据。

然而,WebRTC 的安全性并非万无一失,其脆弱性主要集中在基础设施层面。最著名的安全问题是 IP 地址泄露。由于 ICE 协商需要交换候选者的 IP 地址,即使用户使用了 VPN,其真实的本地和公网 IP 仍可能暴露给通信的另一方,这严重损害了隐私。

缓解此问题的方法是在网络拓扑中强制使用 TURN 服务器中继,从而隐藏客户端的真实 IP。信令通道的安全性至关重要,如果信令服务器未使用 WSS (WebSocket Secure) 或 HTTPS,攻击者可以拦截信令消息,从而发起会话劫持或拒绝服务攻击。

此外,TURN 服务器若配置不当,可能被滥用进行 DDoS 攻击或数据窃取。

最后,尽管 WebRTC 协议本身很安全,但其浏览器实现可能存在漏洞,需要及时更新浏览器以修补已知的安全问题。

6

 

8、架构设计与可扩展性挑战

为 LLM 应用选择实时通信技术不仅关乎功能实现,更深刻地影响着系统的整体架构设计和未来的可扩展性。本章节将深入探讨使用 Server-Sent Events (SSE)、WebSocket 和 WebRTC 时面临的架构挑战,特别是在大规模生产环境下的可扩展性问题。

8.1 Server-Sent Events

使用 Server-Sent Events (SSE) 的架构相对简单且更具弹性。

由于 SSE 基于标准的 HTTP,它可以轻松地与现有的无状态微服务架构集成。服务器可以水平扩展,因为每个 SSE 连接都是独立的,负载均衡器可以将新连接分发到任何可用的服务器实例。

然而,当需要向所有订阅用户广播消息时,简单的无状态架构会遇到挑战。解决这个问题的常见模式是引入一个中央消息代理,如 Redis Pub/Sub 或 Kafka。所有服务器实例都订阅同一个 Redis 频道,当有新消息需要广播时,只需将消息发布到该频道,Redis 会将其高效地分发给所有相关的 SSE 客户端。这种发布/订阅(Pub/Sub)模式极大地简化了广播逻辑,并提高了系统的可扩展性。

此外,SSE 的 stateless nature 使其非常适合在云原生环境中运行,例如 Kubernetes 或 serverless 平台,因为它不需要在节点间共享会话状态。

8.2 WebSocket

相比之下,WebSocket 的架构设计则充满了挑战,其根本原因在于连接的有状态性。

每个 WebSocket 连接都会占用服务器的一个文件描述符,并持续消耗内存和 CPU 资源。当用户数量从数百增加到数万甚至数百万时,这