Jack Jiang

我的最新工程MobileIMSDK:http://git.oschina.net/jackjiang/MobileIMSDK
posts - 422, comments - 13, trackbacks - 0, articles - 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 实现)。

v9.0 版更新内容

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

(1)Android端主要更新内容适配最新Android系统等多项升级和优化】:

  • 1)[升级] 提升targetSdkVersion至33;
  • 2)[升级] 适配最新Andriod 13+系统的动态权限申请逻辑;
  • 3)[升级] 解决了Android13+系统全面屏手机上,APP下方出现大约15dp的黑色空白问题;
  • 4)[升级] 解决了Android13+手机上无法显示Notification通知的问题(Android13新增了通知权限,需动态申请后才能显示);
  • 5)[优化] 重新优化了闪屏、登录、帮助、忘记密码、注册、注册成功、查找用户、实时语音、实时视频等共计13个界面的UI设计;
  • 6)[优化] 其它未提及的ui细节优化和美感提升。
  • 7)[bug] 解决了从好友列表中打开群聊界面,不显示“返回”按钮的问题。
  • 8)[bug] 解决了当处于群聊界面时,群主更新群名称时,不能同步刷新群聊界面标题上的群名称显示。

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

  • 1)[优化] 解决了桥接模式下与最新rabbitmq库不兼容从而断线重连不成功,导致MQ中消息堆积的问题:
  • 2)[优化] 解决了桥接模式下MQ断线自动恢复时未主动清理Chanel,导致Chanel越来越多的问题;

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

posted @ 2023-07-26 12:53 Jack Jiang 阅读(86) | 评论 (0)编辑 收藏

本文由网易云信李兴分享,原题“深度剖析“圈组”深度剖析“圈组”关系系统设计”,为了提升内容品质,本文收录时有修订。

1、引言

上篇《百万级成员实时社群技术实现(消息系统篇)》中,我们分享了云信“圈组”(“圈组”是云信的类Discord产品实现方案)消息系统的技术设计和实践。

本篇接上篇,将继续分享云信“圈组”的关系系统在技术架构上的设计和实现。希望带给你启发。

 
技术交流:

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

2、系列文章

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

3、作者介绍

李兴:网易云信资深服务端开发工程师,毕业于浙江大学,硕士毕业后加入网易,负责云信 IM等业务的服务器开发。专注于即时通讯以及相关中间件等技术。

4、“圈组”的关系业务特点

4.1概述

在互联网行业盛行一句话,技术是为业务服务的。具体到技术实践中,一个重要方面就是要面向业务特点设计技术方案。

因此,想要了解“圈组”的关系系统设计,就要首先了解“圈组”的关系业务特点。

4.2业务特点

“圈组”的关系业务特点是什么?

  • 1)其一:是关系复杂,即关系主体多、管理机制杂、联动耦合重;
  • 2)其二:是规模巨大,即成员数量可达百万量级、变更批量可达百万量级。

所谓关系复杂,具体来讲:首先是关系主体多。

在“圈组”业务中,关系主体包括:

  • 1)服务器:承载社群关系,负责社群成员关系维护;
  • 2)频道:从属于服务器,承载内容关系,负责内容互动关系维护;
  • 3)身份组:可从属于服务器或频道,承载身份权限关系,负责身份设定和权限配置;
  • 4)频道分组:从属于服务器,又关联一组频道,承载频道模版关系,负责分类频道和共享配置。

其次是:管理机制杂。

在“圈组”业务中,仅就成员管理机制而言:

  • 1)服务器成员采用邀请/申请机制;
  • 2)频道成员采用公开/私密模式+黑/白名单机制;
  • 3)身份组成员采用加入/移出机制;
  • 4)频道分组成员与频道成员采用同步机制。

最后是:联动耦合。

在“圈组”业务中,以频道成员维护为例:频道成员不仅受到公开/私密模式+黑/白名单配置变更的影响,而且会伴随服务器成员变更、身份组变更、身份组成员变更等做联动变更。

所谓规模巨大,具体来讲:

  • 1)一方面:成员数量可达百万量级(在“圈组”业务中,服务器成员数量可以达到数百万人);
  • 2)进一步:百万成员服务器下的频道和身份组,其成员数量也可以达到百万量级;
  • 3)另一面:是变更批量可达百万量级。

所谓变更批量可达百万量级,包括:删除百万成员的服务器/频道/身份组,增删频道/频道分组黑白名单中的百万成员身份组等。

从“圈组”关系业务的两大特点出发,可以发现:“圈组”关系是不同于群组关系的全新业务场景,将会面临全新的技术难点。

5、“圈组”关系系统的技术难点

5.1概述

技术难点主要有两个方面:

  • 1)其一:是多关系主体、多管理机制在层级结构下关联耦合导致的业务逻辑的复杂性;
  • 2)其二:是成员数量、变更批量规模巨大导致的业务处理在时间、空间、资源等开销上的复杂性。

5.2业务逻辑复杂性

1)首先“圈组”有多级结构:

包括服务器/频道二级结构、服务器/频道分组/频道三级结构等。

单个关系主体变更,不仅涉及自身的变更,而且涉及上下级关系主体的变更,可以说牵一发动全身。相比而言,群组是没有层级的,群组变更只要独善其身就好。

2)其次“圈组”有身份组:

一个身份组是一组有共同权限的服务器成员的集合,不同身份组的成员可以相互交叉,身份组会作为整体参与到成员管理中。

也就是说,成员变更不再只是个别成员(1-100人)的进入退出,将会出现整组成员(1-1000000人)的大进大出。相比而言,群组是没有身份组的,群组特殊成员包括群主、管理员等也都数量不多、互不重复。

3)最后“圈组”有多种成员管理机制:

服务器成员和身份组成员的管理机制与群组类似,频道成员和频道分组成员的管理机制却是全新模式。

频道分为公开和私密两种:

  • 1)公开模式默认允许所有服务器成员可见,但要排除黑名单身份组和黑名单成员;
  • 2)私密模式默认不许所有服务器成员可见,但要放开白名单身份组和白名单成员。

除了受到公开/私密模式+黑/白名单配置变更的影响,频道成员也受到所依赖的关系主体(服务器成员、身份组、身份组成员)变更的影响。进一步,频道成员还受到所同步的频道分组变更的影响。相比而言,群组成员的邀请/申请机制,可以说是小巫见大巫。

5.3业务处理复杂性

1)首先是成员数量规模巨大:

由于成员数量可达百万,整个成员列表的存储空间开销、网络传输开销,变得十分巨大,不论全量成员列表数据的服务器缓存,还是全量成员列表数据从服务器到客户端的同步,都将变得难以实现。

2)其次是变更批量规模巨大:

单次接口调用的关系变更,可能伴随百万规模的联动关系变更,这会导致巨大的处理时间开销、计算资源开销,不论所有变更同步完成处理,还是所有变更单机完成处理,都将变得难以实现。

3)最后是通知消息规模巨大:

关系系统不仅需要做关系变更的数据处理,而且需要通知变更结果到客户端。由于在“圈组”中各个关系主体的成员数量规模巨大,使得单个变更需要扩散为百万通知同时下发,所需计算资源开销、网络传输开销十分巨大。

相比而言,群组方案因为成员数量、变更批量规模有限,并不涉及这些技术难点。

从“圈组”关系系统的两个方面技术难点出发,可以发现:“圈组”关系系统面临不同于群组的全新技术难点,想要解决这些技术难点,需要创新的技术方案。

6、“圈组”的整体架构

“圈组”方案的整体架构:

上面展示了“圈组”方案的整体架构,可以看到“圈组”整体是一个分层架构。

从上到下看:

1)客户层:包括可供客户端集成的移动端、桌面端、跨平台 SDK,和可供服务器调用的 OpenAPI;

2)接入层:包括 LBS 服务、长连接服务和 API 网关,分别对应客户端 SDK 和用户服务器;

3)网络层:包括自研的全球实时传输网络 WE-CAN;

4)业务层:包括用于 SDK 业务处理的 App 服务和用于 OpenAPI 业务处理的 WebServer 服务;

5)服务层:划分有登录、消息、关系、身份组、支持等服务模块,每个服务模块包括有多个微服务或消费者;

6)基础设施层:包括系统所用的数据库和中间件。

7、“圈组”关系系统的架构

上图展示了“圈组”关系系统的技术架构。可以看到“圈组”关系系统遍及“圈组”架构的接入层、网络层、业务层和服务层。

从功能出发整体上分为三个部分:

  • 1)关系操作同步处理模块;
  • 2)关系事件异步处理模块;
  • 3)变更通知在线广播模块。

下面具体讨论三个方案要点的技术细节,包括频道成员关系管理、变更通知在线广播和关系数据云端检索。

8、关系系统技术实现1:频道成员关系管理

频道成员关系管理,是“圈组”中极具挑战性的问题。

频道成员涉及多关系主体、多管理机制、联动变更耦合严重,成员数量和变更批量规模巨大,可以说是“圈组”关系业务的典型代表。

频道成员关系管理在业务逻辑和业务处理两方面的复杂性可想而知。

针对频道成员关系管理问题,“圈组”设计了两大机制加以解决。

包括:

  • 1)终态维护与过渡计算相结合机制;
  • 2)事件按序异步并行处理机制。

终态维护与过渡计算相结合机制,具体来讲:频道成员关系数据最终被维护在持久化数据库中,并在频道成员没有变更的终态阶段,直接支持频道成员数据的查询需求。当频道成员发生变更时,由于变更逻辑和变更处理两方面的复杂性,完成关系变更需要一段时间,称之为过渡阶段。

在过渡阶段,数据库持久化的频道成员表数据是不完全准确的,无法直接支持频道成员数据的查询需求。此时转为由频道成员配置元数据直接计算频道成员以支持查询需求。因为频道成员配置元数据的变更是同步处理的,所以在过渡阶段由频道成员配置元数据直接计算频道成员可以保证查询准确性。通过将频道成员关系管理分为终态和过渡两个阶段,并在不同阶段采用不同频道成员查询方案,不仅解决了单纯由计算获取频道成员资源开销大的问题,而且解决了频道成员变更延迟导致由数据库获取频道成员结果不准确的问题。

除了频道成员的获取查询问题,频道成员的变更处理也很重要。

事件按序异步并行处理机制,就是用于解决频道成员的变更处理问题:

  • 1)其一:通过将影响频道成员关系的变更操作分层级、系统化定义为变更事件,显著降低频道成员关系管理的业务逻辑复杂性;
  • 2)其二:通过 ID 哈希、分布式锁、事件版本号控制等保证变更事件的按序处理,有效避免事件处理乱序导致的持久化数据错误;
  • 3)其三:通过消息队列中转事件并在消费者上异步处理,有效解决联动变更批量过大导致接口调用阻塞的问题;
  • 4)其四:通过在单个事件处理中的多线程并行加速和本地缓存重用加速,显著缩短频道成员关系变更的时间延迟。

9、关系系统技术实现2:变更通知在线广播

关系系统不仅需要做关系变更的数据处理,而且需要通知变更结果到客户端。

在百万量级的“圈组”关系中,每条关系变更通知,都会面临海量扩散的接收者。除了通知分发量激增,不同接收者对于通知接收的缓急差异也值得关注。

针对变更通知在线广播问题, 我们设计了两大机制:

  • 1)变更分类通知机制;
  • 2)数据通知拉取机制。

在变更分类通知机制中:一方面,根据相关人员在变更中的角色,划分为参与者和观察者分类做通知,即参与者一定通知,观察者按照订阅需求通知。其中参与者一般是变更中的少数关键人员,观察者则是除了参与者之外可以看到变更结果的其它人员。通过分类通知,不同接收者对于通知接收的缓急差异得到合理关注,变更通知的扩散规模也得到精准缩小。

另一方面,观察者按照订阅需求通知,可以充分发挥“圈组”的在线广播订阅模式的优势。所谓在线广播订阅模式,是指在用户登陆之后,需要订阅感兴趣的服务器/频道的通知,“圈组”系统会记录下这些订阅信息,当有新的通知时,“圈组”系统通过订阅关系而非成员列表 + 在线状态获取需要在线广播的用户列表,从而不再需要遍历服务器/频道的所有成员及其在线状态。通过采用在线广播订阅模式,不仅显著降低变更通知在线广播的计算开销和带宽开销,而且可以实现变更通知在线广播在长连接服务集群的并行加速和水平扩展。

变更通知的最终目的是将变更后的数据给到客户端:不同于群组,“圈组”并不将变更后的数据直接由通知带给客户端,而是采用通知客户端有变更再触发客户端拉取结果数据的机制。

究其原因,不同于群组将关系数据全量同步到客户端,“圈组”客户端不再存储关系数据的全量镜像,因此不再需要通过全量历史 + 增量变更的方式维护客户端上的关系数据全量镜像。

与此同时,订阅变更通知的观察者也并不是每时每刻都要关心变更的结果数据,关心某次变更结果数据的观察者相比订阅变更通知的观察者在数量上会少很多,因此,数据通知拉取机制会显著降低变更通知的资源开销。

另外,相比带变更数据通知,只通知有变更,便于直接合并相同类型的通知,而不用关心合并变更数据存在的时序、并发等问题,如此,数据通知拉取机制可以通过短时间内通知合并显著降低服务器在线广播开销和客户端通知接收开销。

10、关系系统技术实现3:关系数据云端检索

在“圈组”中,伴随关系规模的大幅增长,群组基于应用服务器全量查询关系数据或客户端全量同步关系数据实现精准查询和灵活排序的方案不再适用。

对此,“圈组”采用了关系数据云端检索的方案。

“圈组”关系数据云端检索方案可支持服务器、频道、成员等的检索能力。

从检索场景上分,包括:

  • 1)广场检索:用于检索感兴趣的服务器。可以根据名称、类别等多种维度检索。检索结果可以根据预定义字段(成员数量等)或自定义值(数据热度等)等进行排序;
  • 2)内部检索:用于检索用户可见的服务器、频道、成员等。可以根据名称、昵称等多种维度检索。检索结果可以根据预定义字段(创建时间等)或自定义值(数据热度等)等进行排序。

11、相关资料

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

[2] 以微博类应用场景为例,总结海量社交系统的架构设计步骤

[3] IM开发技术学习:揭秘微信朋友圈这种信息推流背后的系统设计

[4] 直播系统聊天技术(四):百度直播的海量用户实时消息系统架构演进实践

[5] 喜马拉雅亿级用户量的离线消息推送系统架构设计实践

[6] 企业微信客户端中组织架构数据的同步更新方案优化实战

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


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

posted @ 2023-07-21 14:56 Jack Jiang 阅读(86) | 评论 (0)编辑 收藏

本文由网易云信资深服务器开发工程师曹佳俊分享,原题“深度剖析“圈组”消息系统设计 | “圈组”技术系列文章”,为了提升内容品质,本文有修订和删节。

1、引言

鉴于实时社群产品Discord在IM垂直应用领域的爆火,类似的需求越来越多,云信的“圈组”就是针对这种应用场景的技术产品。

“圈组”产品发布后获得了很大的关注,很多云信用户在接入SDK的同时对于“圈组”的底层技术细节和原理也非常关注,为此我们决定推出“圈组”相关的技术文章,分享云信在“圈组”技术设计上的一些思考和实践。

本文是序列文章的第2篇,将要分享的是云信的实时社群产品“圈组”(“圈组”云信的类Discord产品实现方案)的消息系统技术设计实践。

 
 技术交流:

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

2、系列文章

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

3、作者介绍

曹佳俊:网易云信资深服务器开发工程师,毕业于中国科学院,硕士毕业后加入网易,负责云信 IM/RTC 信令等业务的服务器开发。专注于即时通讯、RTC 信令以及相关中间件等技术,是云信开源项目 Camellia 的作者。

4、“圈组”的技术特点

在介绍“圈组”的技术细节之前,我们先了解一下圈组的技术特点。

“圈组”产品最大的特点是什么?

  • 1)首先:是 server/channel 的二级结构;
  • 2)其次:是构建在二级结构之上的大规模社群(单个 server 数十万甚至上百万成员);
  • 3)以及:使用复杂的身份组系统来管理如此规模的社群组织和成员。

那么对于这样一个新颖的 IM 系统,在技术上应该如何实现呢?

5、“圈组”和传统IM群组的技术差异

5.1概述

一种简单的思路是改造已有的 IM 系统,对于“圈组”这样的类 Discord 社群,第一个思路是拓展我们的群组功能,猛一看在很多方面确实挺像的。

我们做了个简单的对比:

从上面的表格可以看到,“圈组”和群组最大的不同:

  • 1)是容量的区别;
  • 2)是二级结构。

其他的诸如身份组、个性化推送策略,似乎只要适配的做一下就可以了。

那么是不是只要想办法提升一下群组的容量,再在业务层封装一下二级结构就可以了呢?

答案显然是否定的,或者至少说基于群组去扩展不是一个很好的想法。

5.2二级结构的差异

首先是二级结构。

在类 Discord 的二级结构中,成员的管理在 server 层,而 channel 成员是继承自 server 的,而且在 channel 之上还有很多可见性的配置(我们的“圈组”提供了黑白名单机制,而Discord 则提供了查看频道权限)。

在这种机制之下,任何 server 层面的成员变动,都可能影响全部或者部分频道的成员列表。

面对这种复杂的结构,群组有两种思路去实现:

  • 1)一种是 N 个群,逻辑上隶属于同一个 server;
  • 2)一种是一个群映射为一个 server。

不管哪种方式,先不说消息投递这块的逻辑,仅成员管理上逻辑的耦合和交织的复杂性,足以劝退任何人。

5.3容量的差异

常规IM群组的容量一般只有数百,最多可以扩展到数千。

对于IM群组成员的管理,我们一般采取全量+增量同步相结合的方案,客户端和服务器映射到相同的群组镜像(群信息+群成员等)。此时很多操作,例如群成员的展示、检索,消息的艾特等,都可以基于纯客户端进行。

而“圈组”要求几十万甚至上百万的容量,显然客户端无法一次性获取到所有成员,如果你一次性加入多个 server,那成员的数量将更加膨胀。

因此在“圈组”这种大规模社群的设计中,很多逻辑都会转向云端,此时不管是 SDK 还是服务器,均需要修改原有的设计逻辑。

5.4消息规模差异

此外,大规模社群带来的是消息爆炸。

在原有的IM群组设计中,假设一个人同时加入了 1000 个群,那么这 1000 个群内的所有消息均会在第一时间下发给给客户端。

但是在一般的业务场景中,不会所有的群都同时活跃,假设这 1000 个群变成了 1000 个服务器/频道,作为一种社群组织,同时活跃的可能性将大大增加,而且每个服务器/频道的人数远远超过普通的群组,叠加之后带来的消息爆炸现象在原有的群组体系中将带来极大的压力。

压力包括多方面:

  • 1)首先是海量消息的存储压力;
  • 2)其次是海量消息在线广播/离线消息推送带来的带宽和服务器压力;
  • 3)以及客户端在面对大量消息冲击时如何有效地接受和合理的展示。

5.5小结

除了容量、二级结构、消息规模,包括身份组、成员管理、个性化推送策略等等都存在巨大差异。

是否真的适合在群组中添加这些复杂逻辑呢,强行绑定在一起会不会既没有一个好用的类 Discord 平台,也使得原始的群组功能繁杂,反而降低了易用性呢?

经过上面的一些分析,我们基本可以得出一个结论:在已有的群组基础上扩展来实现一个类 Discord 功能的社群,显然不是一个很好的思路。

那么还有其他“捷径”吗?

IM聊天室也是一个潜在的选项,聊天室的一大特点就是支持超大规模同时在线(参见《千万级实时直播弹幕的技术实践》),容量似乎已经不是问题,但是当考虑添加其他一些强社交关系的特性时(如成员、身份组等)就显得有点为难了,聊天室本身就是来去自如的一个开放空间,这个和圈组的产品本身定位互相冲突的。

因此基于聊天室扩展的方案也基本 pass 掉了。

6、“圈组”的技术难点

基于上述种种的思考和讨论,最终选择脱离已有 IM 体系,从零研发一套全新的社群方案“圈组”,“圈组”不是一个简单的 IM 功能,而是一套可以独立运行的 IM 系统。

经过上面的讨论,相信大家对“圈组”本身的技术特点和难点也有所理解。

可以归纳为以下几点:

  • 1)二级结构下成员无上限的社交关系系统设计;
  • 2)超大社群下消息系统设计;
  • 3)复杂高效的身份组系统设计;

7、“圈组”技术实现之整体架构  

“圈组”整体架构:

上面展示了“圈组”服务整体的架构。

可以看到整个“圈组”服务是一个分层的架构:

  • 1)首先是接入层,包括 LBS 服务和长链接服务器以及 API 网关,对应客户端 SDK 和用户服务器;
  • 2)后面是网络层,包括大网 WE-CAN 和协议路由服务;
  • 3)其次是服务层,划分了多个服务模块,每个模块都包括多个微服务;
  • 4)最后是基础设施。

8、“圈组”技术实现之消息系统架构

这其中和消息系统相关联的包括接入层、网络层、以及后端的登录/订阅/消息/检索等模块。

基本架构如下:

消息系统中第一个要讨论的点就是消息的存储和分发方式,包括在线广播、离线推送、历史消息三个维度。

下面几节我们将对消息系统中各模块分别展开介绍。

9、“圈组”消息系统技术实现1:在线广播

对于一般的IM群组来说,在线广播的一般过程是这样的:依次查询群组里的所有人的在线状态,如果在线,则将消息发送给对应的长链接服务器。

显然这种机制无法复制到“圈组”,因为在“圈组”的一个服务器里可能存在超过 100w 的人。

此外:IM聊天室的广播模式也不能直接复用,因为在聊天室架构中,每个长链接映射到一个聊天室,因此当你登录到某个聊天室的时候,你只会收到该聊天室的消息。而对于“圈组”来说,每个用户会同时加入多个服务器/频道,而且会同时收到多个服务器/频道的消息。

针对“圈组”的上述特点:我们设计了消息订阅模式,也就是用户登录之后,需要订阅感兴趣的相关服务器/频道,服务器会记录下这个订阅信息。当有新消息的时候,服务器通过订阅关系(而不是在线状态)查询到需要广播的列表,通过这种方式就不再需要遍历服务器/频道里的所有用户。

但是当一个服务器/频道里在线人数非常多的时候,这个订阅关系仍然是巨大的。

为此:我们设计了一种两层订阅模型,即所有的订阅关系会保存在长链接服务器上(QChatLink/QChatWebLink),同时长链接服务器会定时发送心跳给后端的订阅服务器,心跳信息相比原始的订阅信息会大大简化,比如长链接服务器上会记录账号 A 订阅了某个频道 A 的消息,如果有 1w 个账号,则有 1w 条订阅记录,而心跳信息里只会上报有 1w 个人订阅了某个频道 A 的消息,具体的账号列表则被精简掉了。当一条消息需要广播时,消息服务会访问订阅服务,获取到该服务器/频道被订阅的长链接服务器列表,并依次给该列表中的长链接服务器发送消息下发通知,长链接服务器收到通知后会根据订阅详情再广播给所有客户端。

此外:我们还提供了多种订阅类型,当你非常关心某个频道消息时(比如页面正停留在该频道),此时你可以订阅该频道的消息。对于其他频道,如果你仅仅需要知道该频道有多少条未读消息(或者有无未读消息),则可以选择订阅该频道的未读计数(或者未读状态),此时服务下发时仅会广播精简的消息体用于维护客户端未读计数,并且当未读计数达到一定阈值之后(比如 99+),服务器可以选择不再下发任何通知消息而不影响用户体验。

通过上文介绍的消息订阅模型,极大地提高了超大型的圈组频道/服务器消息在线广播的效率,降低了服务器压力。

除此之外:我们还设计了针对小型频道的特殊策略,对于小型频道,即使不订阅,服务器也会下发消息通知给频道里所有人,从而减轻端侧消息订阅模型的维护成本。针对消息订阅机制本身,后续我们也会根据不同的业务场景,提供更多一站式的策略来帮助降低接入成本,提升整体的易用性。

10、“圈组”消息系统技术实现2:离线推送

在强社交的场景下,离线消息推送对于维持用户粘性+提升产品体验有很大的作用。

从技术角度看的话,主要解决2个问题:

1)第一个是超大型服务器/频道的消息推送的效率问题;

2)另一个是提供足够丰富的推送策略来帮助 C 端用户,避免被过量的推送消息给打扰。

针对第一个问题,我们针对不同规模的服务器/频道采取了不同的策略:

  • 1)对于小型频道:采用类似于群组的消息推送模型;
  • 2)对于大型频道:对于每一条需要推送的消息,会根据目标用户的 ID 进行任务分片,多个节点并行操作,提高推送效率。

此外:分片会采用一致性策略,保证单个用户固定为某些节点,从而提高缓存命中效率。

针对第二个问题,推送策略可以用以下几句话来描述:

  • 1)既关注促活,又保证不打扰;
  • 2)大型 server 是游乐场,只推送与用户相关的重要消息(如 @消息);
  • 3)小型 server 是与朋友相处的小天地,支持消息的全部推送。

并且:未来用户还可以自定义消息的高低优先级,并搭配不同的推送配置(如不同的免打扰配置等),如下图所示。

11、“圈组”消息系统技术实现3:历史消息

历史消息的存储在“圈组”的场景中也需要一些特别的设计。

同样以传统IM群组为例,一般来说消息的存储方式有两种,写扩散和读扩散。在小型的IM群组或者多人会话中,写扩散模式可以简化设计,但是当群组规模扩大到一定程度(如万人群),读扩散就成了选择。

而对于“圈组”这种单个服务器可能上百万人的“群组”中,除了常规的读扩散之外,我们还设计了多级缓存的结构来应对海量的读请求。

基本的存储架构大致如下:

消息的存储主要包括两部分:

  • 1)一部分是消息本身;
  • 2)一部分是未读计数。

首先是写入:对于上述两者,我们都会使用中心化的缓存服务器来存储最近的数据,并使用异步+批量+聚合等手段,通过 MQ 异步落库,从而平衡写入效率(单条写入性能低)和写入读取延迟(异步写入有延迟)的问题,并且针对不同数据类型的特点,我们也选择了不同的存储方案(历史消息使用分布式时间序列数据库,未读计数使用分布式 k-v 数据库),最大化地提升消息存储和查询的性能和效率。

有写就有读,针对读取操作:

  • 1)所有最近的消息和未读计数均会存储在中心化缓存中,并通过先进先出和缓存过期等不同的策略来确保缓存中存储的永远是最新和最热的数据;
  • 2)对于消息 ID 和消息内容本身,中心化缓存中也会有不同的数据结构和过期策略,来平衡缓存命中率和缓存容量消耗;
  • 3)当缓存过期了,如果有关联的读写请求,将会触发缓存的重建,以保证缓存的命中率始终保持在较高水位;
  • 4)当有高频的读请求,还会触发热点 cache 的检测,并将一部分读请求下沉到各个计算节点的内存中,以应对突发流量的冲击。

上述针对“圈组”的特别设计,消息存储系统可以应对几十数百人的小型圈组频道,也可以从容应对上百万的超大型频道。

12、相关资料

[1] IM群聊消息究竟是存1份(即扩散读)还是存多份(即扩散写)?

[2] 网易云信技术分享:IM中的万人群聊技术方案实践总结

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

[4] 融云IM技术分享:万人群聊消息投递方案的思考和实践

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

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

[7] 千万级实时直播弹幕的技术实践

[8] 深度解密钉钉即时消息服务DTIM的技术设计

[9] 深度揭密RocketMQ在钉钉IM系统中的应用实践

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

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

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

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

posted @ 2023-07-12 13:42 Jack Jiang 阅读(76) | 评论 (0)编辑 收藏

本文由腾讯产品体验设计师volihuang分享,原题“千万级增长,实时社交产品Discord拆解”,本文收录时有内容修订和大量排版优化。

1、引言

对于大多数人而言,对即时通讯IM应用的认知仍然停留在微信、QQ这类经典的即时通讯聊天场景。

实际上,如今的即时通讯技术已渗透到各种业态中,包括本系列文章将要分享的目前大热的Discord实时社群软件(Discord主要用于游戏社交),研究Discord软件包括技术实现上和产品定义上或许可以对你在其它业态中更好的应用即时通讯技术带来启发,也这是整理分系列文章的初衷。

本文为系列文章的首篇,文章内容不讨论Discord具体的技术实现,仅从其产品定义的角度上对Discord软件进行详尽和具体的介绍,希望能帮助你对Discord从产品形态上有较为完整的认知,也方便你阅读本系列文章的后续篇章。

 
 技术交流:

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

2、系列文章

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

3、Discord是什么

3.1席卷游戏圈的社群

Discord是一家游戏实时聊天应用与社区,Discord从游戏语音、IM工具服务起家,随后转向直播平台,进而开设游戏商店的社区平台,成为游戏玩家在游戏中沟通协作的首选工具。Discord于2015年5月公开发行。

在 2018 年,它就已经席卷游戏圈,成了最受游戏玩家亲睐的「语音聊天工具」。在“英雄联盟”美服,几乎每局游戏开始前,都会有人发送 Discord 频道链接,邀请队友通过 Discord 沟通,而不是使用游戏内置的语音工具。

从语音聊天工具,到游戏玩家社区,Discord 似乎正在开创一种全新的互联网社会形态。它预示了一种比 reddit、Facebook 可能更理想的全新未来。

3.2从「工具」到「社区」

Discord 绝不是最「简单易用」的一个,但 Discord 却在思考如何从最底层优化产品,给到用户更多「可能性」.在疫情的大环境下,从2020年2月到7月,Discord的用户数量增加了47%,学习小组开始使用Discord;老师用它上课;朋友们用它来玩,就像平时放学后或周末一样。

3.3UI界面概览

4、Discord的发展历程

Discord相较于传统图文沟通模式的社群有着显著的优点:在Discord上社区建立者可以通过权限设置,轻松的进行用户细分,精准高效的传递信息;也可以进行社交媒体整合,为自己的其他社群进行引流。而Discord建立如此丰富的功能主要分为三个阶段来实现:

4.1第一阶段:游戏语音工具

核心增长点:极致的基础用户体验。

在工具阶段,Discord不断打磨全面超越竞品的基础体验,从界面审美、多端支持、延迟、降噪等等方面都处于市场领先地位。

通过极致的用户体验与因此收获的口碑传播,获取了第一批深度的种子用户。而这些用户逐渐围绕所玩的游戏形成了游戏社群。

4.2第二阶段:游戏社群

核心增长点:平台设计+能力开放+内容运营+用户质量。

在游戏社群阶段,Discord通过平台设计、能力开放、内容运营等方式加速了游戏社群的形成和壮大,游戏品类用户需求的溢出创造了更多的品类。

平台设计:完全免费设计、PC/Web/移动多端支持、免注册即可使用、无任何广告等,这些产品设计加速了用户的裂变;好友列表、加入服务器等沉淀的关系链继而让用户继续留存。Discord在产品设计中始终按照做一个平台的思路来设计,期望快速获得大量用户以形成网络效应。

能力开放:开放了较多的API能力,如支持游戏厂商接入语音sdk、支持同步Twitch直播状态、同步Steam游戏状态等等。这给用户和其他平台方提供强大的额外价值。

如音视频流可直接接入Discord,在服务器内就可以和好友一同观看Twitch/Youtube。如得知好友的游戏状态可以快速加入相同游戏一起开黑等。这也是平台设计的思路,开放能力接入第三方以获取赋能。

4.3第三阶段:全品类社群/社区

核心增长点:强大的管理能力(机器人开放平台/服务器权限/服务器模板…)。

Discord中服务器的管理能力非常丰富,通过设置不同的频道组和频道、设置身份权限、引入机器人等等手段,数十万人的社群也能够进行得有条不紊。

5、Discord的现状

现阶段,Discord 估计有 3 亿用户,其中包括 1.5 亿月活跃用户,平台上有 1900 万个服务器,涵盖游戏、投资、政治、动漫等领域。2020 年,Discord 每周有 670 万服务器处于活跃状态,基本上每周都有某个给定话题的对话讨论。2021 年,Discord 每周活跃服务器数据增长到了 1900 万。

来自移动产业数据平台 Apptopia 的消息显示,线上社区 App「Discord」的下载总量在近期已突破 5 亿次,同时应用内购营收总额突破 1 亿美元。

Discord 平台上单个日活跃用户(DAU)与平台的平均互动时长,是游戏直播平台 Twitch 的两倍,同时还是 Facebook Gaming、TikTok、Reddit 以及 Snap 等头部社交平台的两倍以上。

但是,即便在如此惊人的增长之后,Discord 似乎并没有太多商业化的动作。2020 年,Discord 的每用户平均收入 (ARPU) 仅为 1.30 美元,在公共社交媒体公司中排名非常靠后。


6、Discord平台机制介绍

6.1基本

Discord以其多样化的平台机制设定,为使用者提供了多种多样免费的功能。

它们是:

  • 1)以高音质、几乎零延迟、无限时间与尽可能多的朋友交谈;
  • 2)只需单击两次,即可将游戏直播带给服务器中的任何人,而且不会存在任何延迟;
  • 3) 使用单独的音量滑块一次观看多个流媒体;
  • 4)可以创建几乎无限量的文本聊天室,甚至可以追溯到几年前的档案;
  • 5)与朋友分享小文件;
  • 6)将机器人融入其中,可以向所有人广播音乐;
  • 7)Discord 支持视频流和屏幕截图等功能。

下面,我们详细介绍Discord中的功能设置。

6.2服务器机制

在 Discord 中有一种别于一般通讯软体之群组的群体聊天,称作服务器(类似社团),服务器拥有者可以在服务器中创造属于自己的社群。

例如:MINECRAFT在Discord的服务器,成员数已超过100w人,达到Discord目前设置的服务器上限。

MINECRAFT界面:

此外,在服务器搜索界面搜索MINECRAFT,可发现Discord上有6000+个MINECRAFT相关服务器,分布于社交、娱乐、同好、动画漫画、创作者等不同板块,绝大部分由玩家自发成立,在其中分享素材及想法。

6.3身份组机制

在 Discord 中可以建立非常多不同的身份组,使用者可以完全自订身分组的颜色、名称、权限、符号等等,身份组会直接影响使用者的名称颜色及用户列表的排序。

6.4频道机制

在伺服器中可以建立名为频道的聊天管道,分为语音、文字,其中的语音频道可以用来直播游戏与聊天等,频道可以设定与身份组整合各种权限,让 Discord 社群系统更加多样化。

文字方面:Discord 使用markdown语法,目的是对富文本一定程度的支持。

语音方面:Discord 使用opus音频格式,目的是压缩语音来降低延迟。

“哈利波特:魔法觉醒”的频道介绍列表:

6.5用户机制

每个 Discord 用户都有一个唯一的四位个人识别码,用户名后有一个"#"(例如ABCD#1234)。这使得多个用户能够拥有相同的用户名,并且用户可以很容易地找到朋友。

用户信息示意图:

6.6机器人

在 Discord 中所有使用者皆可以创立机器人,机器人主要是使用 Python 和 Java 编写,透过 Discord API 的语法扩充来编程。机器人可以发送讯息、图片、嵌入式讯息、嵌入式按钮、新增反应等,大致上与人类使用者权限无太大差异,不过在机器人的名称旁会有一个蓝色的 BOT 标志。机器人一样受到身份组权限的控管。

Topwar中的机器人消息及调用指令:

6.7整合

每个频道皆可以使用Webhook来抓取其他资讯,这使得在使用时甚至可以将Facebook、微博的贴文直接同步到Discord的频道中,另外频道也可以追踪另一个公告频道,来直接同步公告频道中的所有讯息。

6.8软件技术

尽管 Discord 的服务器由于其分布式特性无法匹配对应的传统硬件或虚拟服务器,不过其服务器和频道仍可类比于因特网中继聊天技术。用户可以在 Discord 上创建服务器并设定其他用户的加入条件。

Discord 的客户端使用Web技术构建在 Electron 框架上,这使得它可在多平台运行,既可在网页上运行,又可在个人计算机上作为应用程序运行。除了从 Discord 游戏商店下载和玩游戏为 Windows 独有之外,客户端的所有版本都支持相同的功能集(不包括与桌面音频的屏幕共享)。

Discord 是专门设计用于游戏互动的软件,因为它包括诸如低延迟、用户免费语音聊天服务器和专用服务器基础设施等功能。

6.9与游戏互联

在服务器和用户的层面上,Discord 允许用户连接到 twitch 或其他游戏账号。这种集成方式在一些应用程序中提供了独特的消息传递方法。

例如:如果用户使用自己的账号登录steam 玩游戏,Discord 便可以确定该用户正在玩该游戏。

6.10Nitro

虽然软件本身是免费的,但开发人员致力于研究如何将其商业化以营利,以Nitro计划的方式为对emoji和、贴图、个人化个人资料页面、语音及直播画质提升及文字字数限制进行付费使用。

7、Discord中的用户角色

Discord中的角色为用户提供特定权限。

例如:可以为主持人创建一个角色,并为该角色授予禁止用户和删除邮件的权限。 分配给该角色的任何用户都将继承这些权限。 使用角色可以使不必为每个用户分配权限。

要管理角色,请打开服务器设置,然后单击左侧的“角色”类别。 可以通过单击页面上“角色”标题侧面的小加按钮来添加新角色。 选择一个角色来管理权限。

有很长的权限列表,但重要的权限涉及通过创建新的渠道或角色来管理服务器的能力,通过禁止或删除邮件来管理用户,以及将用户移入和移出语音聊天。

还有一个管理员角色,它提供除服务器所有者特定的权限之外的所有权限(例如:删除服务器)。

8、Discord中的频道

服务器上的每个频道都按类别进行组织。 要创建新通道或类别,请右键单击通道窗格中的任意位置,然后单击“创建通道”或“创建类别”命令。

创建频道时,请为其命名并选择是应该是文字频道还是语音频道。 通道名称不能包含空格(键入空格只会创建连字符)或大写字母。

频道也有自己的频道特定权限,可以通过单击频道旁边的齿轮来访问这些权限。 这些权限默认与频道所属的类别同步,但如果更改它们,它们将保持这种状态,直到再次同步。

还可以将类别和频道设为私有。 当创建频道时,只需选择“私人频道”,然后启用希望能够访问该频道的角色。

如果只想向频道添加一些人,最好为该频道创建一个新角色,然后将用户添加到该角色。

下面我们讲介绍常见频道类型。

8.1)欢迎频道/规则频道:

欢迎频道一般包括服务器及游戏内容的大概说明、禁止的事项、频道发言规范等信息。可以由公告或文本频道设置而成。也可再次频道设置本地化相关选项(如语言)。

8.2)游戏活动公告频道组:

官方针对游戏内活动及社群相关活动的推宣,以公告频道的形式呈现。

8.3)游戏直播、其他社交媒体链接:

可以选择同步自身的twitter也可将自身所有媒体链接以消息的形式呈现,有助于游戏自身社交平台间的相互引流。

8.4)二创内容频道组:

通常包含玩家的绘画创作、视频创作、cosplay等,官方可在此频道中发布相应活动的信息并发放相应的活动奖励。

8.5)玩家公共讨论区频道组:

设置所有玩家都可参与的公共频道,为玩家提供交友、游戏内容交流甚至闲聊的空间。

8.6)语音讨论频道组:

为玩家建立可公共使用的语音频道,方便玩家与好友进行组队语音交流

8.7)娱乐频道组:

歌房:一起听歌的语音频道。

9、Discord中的机器人

除了聊天功能和社交架构之外,Discord 平台最引人注目的部分可能是其蓬勃发展的机器人生态系统。

在 2020 年的一篇博文中,Discord 宣布已经创建了超过 300 万个机器人,其中一些已经在数百万个服务器端上使用。

机器人举例:

  • 1)MEE6 是一个特别受欢迎的机器人应用,超过 1400 万服务器使用它来创建自定义欢迎消息、主动引导不良行为者、分配社区角色、并为积极参与社区活动的用户授予“XP”(“经验点”);
  • 2)ldleRPG 是一个提供更多创意服务的机器人应用,一旦它与服务器集成,社区成员就可以参与角色扮演游戏,这个游戏风格与《龙与地下城》相似,而且可以通过聊天命令参与。

从用户的角度来看,Discord 的机器人生态系统其实非常重要,因为可以扩展功能并增加游戏感。

而站在企业角度来看,机器人生态系统能从业务层面提供支撑,因为它允许开发人员在其应用程序接口(API)上进行构建。

10、Discord带来的启发

Discord背后的模式值得以社交的视角进行借鉴,辅助游戏端外社群运营。

1)首先:学会给用户创造一个新习惯,融入用户的生活场景,让用户对社区产生粘性。

Discord在提供给游戏玩家一个新的实时通话的社交平台的同时,其实是在给用户培养一个新的使用习惯,培养出来有社交互动需求的用户在玩游戏的时候,会的使用discord的习惯。

在培养用户的使用习惯以及粘性的这个过程中,需要团队专注于解决用户的核心需求,并且持续的提供技术支持。也就是要专注做好一个社交平台应该做的事情。

国内其实也有一个很好的例子。早年中国也有本土产出的用于服务游戏玩家“开黑”这定需求的社交软件,比方说,早年新浪上线的语音聊天产品UT,同期的在线群聊产品,以及后来的黑马YY语音。

2)其次:专注于解决用户的核心需求,找准定位,求同存异,保持用户的好感度。

从Discord的案例来说,它一开始的定位非常明确,就是小而精,针对于游戏群体的实时通话软件,然后在不断的完善功能的同时,扩大用户群体然后迅猛增长。

Discord专注于提升用户的体验并且保持用户社交的私密性,解决了解决用户的核心需求——网络实时社交。

11、相关资料

[1] 快速了解新一代跨平台桌面技术——Electron

[2] 盘点移动互联网时代的社交产品进化史(上篇):谁主沉浮

[3] 盘点移动互联网时代的社交产品进化史(下篇):大浪淘沙

[4] 中国互联网社交二十年:全民见证的互联网创业演义

[5] 别做梦了,社交产品哪有那么容易成功

[6] 同为IM社交产品中的王者,QQ与微信到底有什么区别

[7] 渐行渐远的人人网:十年亲历者的互联网社交产品复盘和反思

[8] 即时通讯创业必读:解密微信的产品定位、创新思维、设计法则等

[9] QQ的成功,远没有你想象的那么顺利和轻松

[10] 同为IM社交产品中的王者,QQ与微信到底有什么区别

[11] 还原真实的腾讯:从最不被看好,到即时通讯巨头的草根创业史

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

posted @ 2023-07-07 12:13 Jack Jiang 阅读(92) | 评论 (0)编辑 收藏

本文由云信IM技术团队分享,原题“千万级在线直播弹幕方案”,本文有修订和改动。

1、引言

疫情期间,线上演唱会是一种很常见的直播娱乐形式,由于线下社交距离的限制,线上形式演唱会比以往更火爆,而对技术的要求也更高。

本文基于网易云信针对TFBOYS某场线上演唱会的技术支持,为你分享千万级在线用户量的直播系统中实时弹幕功能的技术实践,希望能带给你启发。

 

技术交流:

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

2、系列文章

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

3、弹幕整体技术方案

本次的弹幕方案以IM聊天室技术为基础,提供了登录直播间、发送弹幕、礼物消息等能力。同时按照千万级在线广播的目标,为期设计了基于CDN的弹幕广播服务。

直播间收发实时消息(也就是弹幕、礼物)的主要流程如下:

  • 1)获取直播间接入地址;
  • 2)登录直播间;
  • 3)收发消息(弹幕、礼物)。

下面将围绕以上流程的三个阶段,在技术上分别阐述如何实现千万级在线直播实时弹幕的能力。

4、弹幕技术方案之获取直播间接入地址

为了提供稳定高可用的实时弹幕服务,需要通过GSLB(Global Server Load Balancing)服务给用户分配最佳的接入地址。

GSLB服务需要从以下几个维度综合考虑:

  • 1)用户网络类型;
  • 2)机房网络容量;
  • 3)服务器负载;
  • 4)成本。

1)用户网络类型和机房网络容量:

为了用户能够快速、稳定的登录直播间收发消息,一般要根据用户所在地理位置以及网络运营商类型综合考虑给用户分类接入服务器。

机房一般提供BGP网络、三线网络两种接入方案,分配服务根据用户IP地址分析用户的地域、运营商类型并分配最佳接入地址。

一般优先按运营商类型分配三线地址(例如电信用户分配电信接入地址),如果是小运营商或无法识别的IP地址则分配BGP地址,两种接入方式用户都可以获得稳定的网络环境。

2)服务器负载:

单台服务器能够承载的TCP长链接有限,尤其是在高并发进入直播间的情况下,握手协议需要完成链路加密工作,对系统的CPU资源消耗比较大,因此需要实现一套良好的均衡分配策略。

3)一套基于服务器负载均衡的分配策略:

长链接接入服务器周期性上报当前服务器负载到负载均衡服务集群,负载信息存储在共享缓存中,接入分配服务根据负载信息动态分配接入地址。

一般情况下用户请求直播间地址,地址分配服务会查询负载均衡服务(或者直接查询负载缓存),然后根据获取到的信息分配当前负载最低的服务器。

在千万级别的在线直播活动场景下,开播时大量用户并发进入直播间,分配服务可达50万到100万TPS,这么高的TPS下如果还用“一般分配”方案,负载均衡(缓存)服务的TPS和集群之间的机房网络带宽非常高,单台服务器亦可能因为负载信息滞后导致超负荷分配。

为解决机房内带宽和超负载分配的问题,我们对分配方案进行了优化:

  • 1)长链接服务器上报负载的周期从1秒调整到5毫秒,负载均衡服务器可以更实时的同步负载信息;
  • 2)“地址分配”服务不再按请求查询负载信息,而是开启单独的同步线程周期性(同样是5毫秒)同步负载数据,从而有效降低负责信息同步的TPS和网络开销;
  • 3)“地址分配”服务不在按最低负载分配,而是将服务接入地址按负载排序,单个接入地址分配一定次数后按顺序分配下一个接入地址(避免低负载服务器瞬间被打爆)。

在实际方案落地中,需要结合负载、用户网络类型、机房线路容量等因素综合分配。

5、弹幕技术方案之登录直播间

登录直播间主要有两项任务:

  • 1)握手;
  • 2)身份认证。

1)握手:

SDK建立TCP长链接后,首先向服务器发送握手协议,主要提供SDK版本、协议版本、支持的加密算法等信息,服务器根据SDK提供的信息选择合适的协议版本以及加密算法,建立安全的通信链路。

我们支持的非对称算法包括:RSA、SM2等算法。支持的对称加密算法包括:AES,SM4等(SM2、SM4为国密算法)。

非对称加密算法对CPU资源消耗非常高,为了提高性能一般可以考虑选择合适的密钥长度,另外针对Java平台建议考虑使用JNI技术提高非对称加密计算性能。

2)身份认证:

引言中提到的该次直播活动是在线付费直播,因此身份认证包含了账号认证和业务认证两部分,即用户必须使用正确的账号密码登录App,且必须付费购买直播门票才有权限观看直播。

为优化系统性能,实时弹幕服务将“地址分配和鉴权”服务进行了特殊优化:

鉴权中心提供用户进入直播间弹幕服务的身份鉴权策略配置。在该次直播活动中采用了动态Token的鉴权机制,即根据用户账号、登录时间、分配的接入地址以及鉴权中心按时间区间生成的“随机数以及对应的Token算法”动态计算鉴权Token。

用户打开直播App,首先完成账号鉴权。在进入直播间时通过业务中心完成直播付费身份认证和弹幕服务地址分配(同步获取到弹幕服务的动态鉴权token),最后根据接入地址登录弹幕服务,弹幕服务依据鉴权中心的策略校验Token正确性。

动态Token鉴权采用进程本地计算的方式。可以在不访问用户服务的情况下完成身份鉴权,在提高登录认证的性能同时有效的降低了业务成本。

6、弹幕技术方案之收发消息(弹幕、礼物)

实时收发消息是直播间的核心业务,主要分为弹幕和礼物两类:

  • 1)礼物因涉及付费等因素一般通过客户方业务服务器发送;
  • 2)弹幕消息则可以通过聊天室长链接发送。

在千万级直播间场景下,因消息量太高,因此需要从消息量、消息体大小、消息比例等多个方面优化,因此我们设计了一套基于优先级队列的弹幕服务。

首先:为了节约消息产生的带宽,在大型直播项目开始阶段,就需要对消息格式进行优化,充分精简消息体大小。例如将礼物消息展示相关的资源文件提前预加载到直播App中,礼物消息转化为业务编号,可极大的减少消息大小;

其次:针对上行消息设计流控机制。为了能全局控制上行消息体量,设计了逐级流控方案。上层级根据下层级能够支撑处理能力设计相对较粗粒度的本地流控机制。在弹幕反垃圾业务阶段,因需要全局控制消息量,因此采用分布式全局流控方案;弹幕广播阶段则根据业务广播需求再一次进行消息流控。

上行消息通过反垃圾监测后被投递到弹幕服务处理。基于优先级队列的弹幕服务首先按业务划分不同的消息队列,例如:系统广播、高优先级礼物、低优先级、弹幕,然后按队列分配消息比例,最后根据单位时间(1秒)内用户需要接收到的消息量计算各个队列应该投递的消息数量。在实际投递消息的过程中,若前一个队列消息量不足,可将剩余的消息数量叠加到下一个队列,以确保每一个周期都发送足够的消息给用户。

弹幕可通过长连接或CDN广播给其他用户。为了给用户提供极致的弹幕体验,充分发挥边缘加速的优势,在千万级在线直播场景下优先选择CDN方案(如下图所示)。

基于CDN广播弹幕有两种方案:

1)基于推流的方案:类似于直播视频推流技术,即将消息伪装成视频流的形式推送到CDN,直播App以订阅数据流的方式同步弹幕信息;

2)静态文件加速方案:即弹幕服务将不同队列中的消息组装成一个静态文件,直播App周期性的到CDN服务器下载弹幕静态文件。

相对来说:

  • 1)静态文件加速方案实现更简单但实时性不高(取决于弹幕同步的周期时长);
  • 2)推流的方案消息实时性更高,但实现相对复杂,且需要考虑到不同终端的兼容性。

实际项目中可根据场景和终端类型灵活选择不同的方案。

为了保障服务的可靠性,可考虑融合CDN的方案,即同时将消息推送到多家CDN厂商,并结合CDN厂商的容量比例以及网络延迟情况综合调度(例如基于权重的轮巡调度策略)。

7、弹幕稳定性设计之单元化部署

ChatLink和ChatServer采用单元化部署的方案,有以下优点:

  • 1)单元内依赖的核心服务单元之间相互独立,水平扩展能力好,且单元内服务故障不影响其他单元,可以有效避免整个服务不可用的问题;
  • 2)跨机房部署,避免单个机房容量不足,或单机房不可用问题;
  • 3)弹幕方案采用了单元无状态的设计理念,因此不需要考虑单元之间同步数据的问题。

单个直播间的“接入服务”和“弹幕服务”因需要全局控制未采用单元化部署方案,但是在实施阶段采用了跨机房部署的方案(包括依赖的存储资源、服务),可以避免单个机房故障导致服务不可用的问题。

8、弹幕稳定性设计之单点服务高可用

针对“接入服务”和“弹幕服务”,除了采用跨机房部署外,在服务设计上核心依赖的存储资源、服务,采用主备模式。

例如:心跳负载依赖的缓存服务,单个缓存实例本身高可用,但考虑到极端情况(例如缓存集群内超过一半的服务器宕机导致服务不可用),因此采用主备缓存集群方案,当主集群不可用后,业务主动切换到备用集群,可保障业务在5秒内恢复正常。

9、幕稳定性设计之系统监控与数据大盘

为了实时了解系统运行状态,在弹幕方案中实现了秒级数据大盘方案。

监控大盘围绕用户和消息主要展示以下信息:

  • 1)用户地域分布变化;
  • 2)上行消息量;
  • 3)广播消息量;
  • 4)机房出口带宽;
  • 5)CDN带宽;
  • 6)消息流控比例;
  • 7)端侧CDN弹幕同步指标(成功比例、延迟状况)。

为了达成秒级监控的目标,数据收集采用了“业务预聚合+数据中心合并”的实时计算方案。即业务服务直接在本地进程内聚合计算指标上报到数据中心,数据中心仅需要按时间窗口合并监控指标数据即可输出到监控大盘。

10、弹幕稳定性设计之故障与应急预案演练

为确保活动顺利完成,弹幕方案还进行了多次故障与应急预案演练措施。

具体包含两个方面。

1)预设故障演练:即针对高可用设计方案的故障演练,按预设有计划的制造故障,主要验证高可用方案是否生效。

2)随机故障演练:无计划的随机制造故障,主要用于检查应急预案、异常监控报警、数据大盘等应急监测机制是否生效。

11、相关资料

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

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

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

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

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

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

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

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

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

[10] IM中的万人群聊技术方案实践总结

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

posted @ 2023-06-29 11:32 Jack Jiang 阅读(77) | 评论 (0)编辑 收藏

为了更好地分类阅读 52im.net 总计1000多篇精编文章,我将在每周三推送新的一期技术文集,本次是第18 期。

[- 1 -] IM系统的MQ消息中间件选型:Kafka还是RabbitMQ?

[链接http://www.52im.net/thread-1647-1-1.html

[摘要] MQ消息中间件可以理解一个水池,水池的这头是消息生产者,水池的那头是消息消费者,生产者和消息者无需直接对接,这将带来很多好处:业务解耦、架构分布式化等,生产者和消费者互相完全透明。但市面上的MQ消息中间件产品很多,作为IM系统中必不可少的一环,我们该如何选型?那么请继续阅读本文。


[- 2 -] 腾讯资深架构师干货总结:一文读懂大型分布式系统设计的方方面面

[链接http://www.52im.net/thread-1811-1-1.html

[摘要] 本文适合有过几年工作经验、正处于技术上升期的程序员阅读,内容少有浮夸,多为实践经验总结,希望能为您的技术成长加油助力。


[- 3 -] 以微博类应用场景为例,总结海量社交系统的架构设计步骤

[链接http://www.52im.net/thread-1910-1-1.html

[摘要] 本文让我们结合典型的互联网应用架构设计原则,通过一个模拟的微博应用场景,和你一起看看在微博这种海量社交应用实践中究竟如何分步进行架构设计的。


[- 4 -]快速理解高性能HTTP服务端的负载均衡技术原理

[链接http://www.52im.net/thread-1950-1-1.html

[摘要] 本文将以简洁通俗的文字,为你讲解主流的HTTP服务端实现负载均衡的常见方案,以及具体到方案中的负载均衡算法的实现原理。理解和掌握这些方案、算法原理,有助于您今后的互联网项的技术选型和架构设计,因为没有哪一种方案和算法能解决所有问题,只有针对特定的场景使用合适的方案和算法才是最明智的选择。


[- 5 -] 子弹短信光鲜的背后:网易云信首席架构师分享亿级IM平台的技术实践

[链接http://www.52im.net/thread-1961-1-1.html

[摘要] 本文内容来自对网易云信首席架构师周梁伟的采访,采访内容主要围绕网易云信这种海量用户IM云平台的关键技术难点以及对应的技术实践。


[- 6 -] 知乎技术分享:从单机到2000万QPS并发的Redis高性能缓存实践之路

[链接http://www.52im.net/thread-1968-1-1.html

[摘要] 知乎存储平台团队基于开源Redis 组件打造的知乎 Redis 平台,经过不断的研发迭代,目前已经形成了一整套完整自动化运维服务体系,提供很多强大的功能。本文作者陈鹏是该系统的负责人,本次文章深入介绍了该系统的方方面面,值得互联网后端程序员仔细研究。


[- 7 -] IM开发基础知识补课(五):通俗易懂,正确理解并用好MQ消息队列

[链接http://www.52im.net/thread-1979-1-1.html

[摘要] 对于即时通讯开发者来说,正确地理解MQ消息队列,对于IM或消息推送系统的架构设计、方案选型等都大有裨益。


[- -]  新手入门:零基础理解大型分布式架构的演进历史、技术原理、最佳实践

[链接http://www.52im.net/thread-2007-1-1.html

[摘要] 即时通讯网作为IM和推送技术研究、学习和分享的社区,整理了大量的跟IM和推广技术有关的基础技术资料(比如网络基础、通信理论、架构基础等),本文内容虽然看起来跟IM和推送技术没有直接的关联性,但因为设计IM和推送系统的技术思路和原理跟典型大型互联网分布式架构都是一脉相承的,因而读懂本文内容对于IM和推送系统的架构设计同样大有裨益。


[- 9 -] 阿里技术分享:深度揭秘阿里数据库技术方案的10年变迁史

[链接http://www.52im.net/thread-2050-1-1.html

[摘要] 今天,阿里数据库事业部研究员张瑞,将为你讲述双11数据库技术不为人知的故事。在零点交易数字一次次提升的背后,既是数据库技术的一次次突破,也见证了阿里技术人永不言败的精神,每一次化“不可能”为“可能”的过程都是阿里技术人对技术的不懈追求。


[- 10 -] 阿里技术分享:阿里自研金融级数据库OceanBase的艰辛成长之路

[链接http://www.52im.net/thread-2072-1-1.html

[摘要] OceanBase 是蚂蚁金服自研的分布式数据库,在其 9 年的发展历程里,从艰难上线到找不到业务场景濒临解散,最后在双十一的流量考验下浴火重生,成为蚂蚁金服全部核心系统的承载数据库。这一路走来的艰辛和故事,蚂蚁金服高级研究员、OceanBase 团队负责人阳振坤将为你娓娓道来。


[- 11 -] 即时通讯新手入门:一文读懂什么是Nginx?它能否实现IM的负载均衡?

[链接http://www.52im.net/thread-2600-1-1.html

[摘要] Nginx(及其衍生产品)是目前被大量使用的服务端反向代理和负载均衡方案,从某种意义上来讲,Nginx几乎是低成本、高负载Web服务端代名词。


[- 12 -] 即时通讯新手入门:快速理解RPC技术——基本概念、原理和用途

[链接http://www.52im.net/thread-2620-1-1.html

[摘要] 本文将带你从基本概念、原理和用途方面,快速理解快速理解RPC技术,以便您在进行IM集群开发时能更好的进行方案设计和实现。


[- 13 -]  多维度对比5款主流分布式MQ消息队列,妈妈再也不担心我的技术选型了

[链接http://www.52im.net/thread-2625-1-1.html

[摘要] 本文将从17个维度综合对比Kafka、RabbitMQ、ZeroMQ、RocketMQ、ActiveMQ这5款当前最主流的MQ消息中间件产品,希望能为您的下一次产品的架构设计和MQ消息中间件选型提供参考依据。


[- 14 -] IM开发基础知识补课(六):数据库用NoSQL还是SQL?读这篇就够了!

[链接http://www.52im.net/thread-2759-1-1.html

[摘要] 本文将分析传统数据库(即SQL数据库)存在的一些问题,以及盘点目前市面上几大类 NoSQL 特性、优缺点等,希望给大家提供一些在不同业务场景下存储技术选型方面的参考。


[- 15 -] IM开发基础知识补课(九):想开发IM集群?先搞懂什么是RPC!

[链接http://www.52im.net/thread-2996-1-1.html

[摘要] 本文将以通俗易懂的白话形式,帮你快速理解IM集群中的关键技术——RPC。


[- 16 -] IM开发基础知识补课(十):大型IM系统有多难?万字长文,搞懂异地多活!

[链接http://www.52im.net/thread-3742-1-1.html

[摘要] 本文从一个简单的系统例子开始,从单机架构、主从副本、同城灾备、同城双活,再到异地双活、异地多活,由浅入深、循序渐进地讲解了大型分布式系统异地多活容灾架构的技术原理和基本的实现思路,非常适合入门者学习。


👉52im社区本周新文:《直播系统聊天技术(九):千万级实时直播弹幕的技术实践 http://www.52im.net/thread-4299-1-1.html》,欢迎阅读!👈


我是Jack Jiang,我为自已带盐!https://github.com/JackJiang2011/MobileIMSDK/

posted @ 2023-06-28 12:23 Jack Jiang 阅读(80) | 评论 (0)编辑 收藏

本文由得物技术团队Uni分享,本文有内容修订和大量排版优化。

1、引言

关于Java网络编程中的同步IO和异步IO的区别及原理的文章非常的多,具体来说主要还是在讨论Java BIO和Java NIO这两者,而关于Java AIO的文章就少之又少了(即使用也只是介绍了一下概念和代码示例)。

在深入了解AIO之前,我注意到以下几个现象:

  • 1)2011年Java 7发布,它增加了AIO(号称异步IO网络编程模型),但12年过去了,平时使用的开发框架和中间件却还是以NIO为主(例如网络框架Netty、Mina,Web容器Tomcat、Undertow),这是为什么?
  • 2)Java AIO又称为NIO 2.0,难道它也是基于NIO来实现的?
  • 3)Netty为什么会舍去了AIO的支持?(点此查看);
  • 4)AIO看起来貌似只是解决了有无,实际是发布了个寂寞?

Java AIO的这些不合常理的现象难免会令人心存疑惑。所以决定写这篇文章时,我不想只是简单的把AIO的概念再复述一遍,而是要透过现象,深入分析、思考和并理解Java AIO的本质。

 

技术交流:

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

2、我们所理解的异步

AIO的A是Asynchronous(即异步)的意思,在了解AIO的原理之前,我们先理清一下“异步”到底是怎样的一个概念。

说起异步编程,在平时的开发还是比较常见的。

例如以下的代码示例:

@Async

publicvoidcreate() {

    //TODO

}

 

publicvoidbuild() {

    executor.execute(() -> build());

}

不管是用@Async注解,还是往线程池里提交任务,他们最终都是同一个结果,就是把要执行的任务,交给另外一个线程来执行。

这个时候,我们可以大致的认为,所谓的“异步”,就是用多线程的方式去并行执行任务。

3、Java BIO和NIO到底是同步还是异步?

Java BIO和NIO到底是同步还是异步,我们先按照异步这个思路,做异步编程。

3.1BIO代码示例

byte[] data = newbyte[1024];

InputStream in = socket.getInputStream();

in.read(data);

// 接收到数据,异步处理

executor.execute(() -> handle(data));

 

publicvoidhandle(byte[] data) {

    // TODO

}

如上:BIO在read()时,虽然线程阻塞了,但在收到数据时,可以异步启动一个线程去处理。

3.2NIO代码示例

selector.select();

Set<SelectionKey> keys = selector.selectedKeys();

Iterator<SelectionKey> iterator = keys.iterator();

while(iterator.hasNext()) {

    SelectionKey key = iterator.next();

    if(key.isReadable()) {

        SocketChannel channel = (SocketChannel) key.channel();

        ByteBuffer byteBuffer = (ByteBuffer) key.attachment();

        executor.execute(() -> {

            try{

                channel.read(byteBuffer);

                handle(byteBuffer);

            } catch(Exception e) {

 

            }

        });

    }

}

 

publicstaticvoidhandle(ByteBuffer buffer) {

    // TODO

}

同理:NIO虽然read()是非阻塞的,通过select()可以阻塞等待数据,在有数据可读的时候,异步启动一个线程,去读取数据和处理数据。

3.3产生的理解偏差

此时我们信誓旦旦地说,Java的BIO和NIO是异步还是同步,取决你的心情,你高兴给它个多线程,它就是异步的。

果真如此么?

在翻阅了大量博客文章之后,基本一致的阐明了——BIO和NIO是同步的。

那问题点出在哪呢,是什么造成了我们理解上的偏差呢?

那就是参考系的问题,以前学物理时,公交车上的乘客是运动还是静止,需要有参考系前提,如果以地面为参考,他是运动的,以公交车为参考,他是静止的。

Java IO也是一样,需要有个参考系,才能定义它是同步还是异步。

既然我们讨论的是关于Java IO是哪一种模式,那就是要针对IO读写操作这件事来理解,而其他的启动另外一个线程去处理数据,已经是脱离IO读写的范围了,不应该把他们扯进来。

3.4尝试定义异步

所以以IO读写操作这事件作为参照,我们先尝试的这样定义,就是:发起IO读写的线程(调用read和write的线程),和实际操作IO读写的线程,如果是同一个线程,就称之为同步,否则是异步。

按上述定义:

  • 1)显然BIO只能是同步,调用in.read()当前线程阻塞,有数据返回的时候,接收到数据的还是原来的线程;
  • 2)而NIO也称之为同步,原因也是如此,调用channel.read()时,线程虽然不会阻塞,但读到数据的还是当前线程。

按照这个思路,AIO应该是发起IO读写的线程,和实际收到数据的线程,可能不是同一个线程。

是不是这样呢?我们将在上一节直接上Java AIO的代码,我们从 实际代码中一窥究竟吧。

4、一个Java AIO的网络编程示例

4.1AIO服务端程序代码

publicclassAioServer {

 

    publicstaticvoidmain(String[] args) throwsIOException {

        System.out.println(Thread.currentThread().getName() + " AioServer start");

        AsynchronousServerSocketChannel serverChannel = AsynchronousServerSocketChannel.open()

                .bind(newInetSocketAddress("127.0.0.1", 8080));

        serverChannel.accept(null, newCompletionHandler<AsynchronousSocketChannel, Void>() {

 

            @Override

            publicvoidcompleted(AsynchronousSocketChannel clientChannel, Void attachment) {

                System.out.println(Thread.currentThread().getName() + " client is connected");

                ByteBuffer buffer = ByteBuffer.allocate(1024);

                clientChannel.read(buffer, buffer, newClientHandler());

            }

 

            @Override

            publicvoidfailed(Throwable exc, Void attachment) {

                System.out.println("accept fail");

            }

        });

        System.in.read();

    }

}

 

publicclassClientHandler implementsCompletionHandler<Integer, ByteBuffer> {

    @Override

    publicvoidcompleted(Integer result, ByteBuffer buffer) {

        buffer.flip();

        byte[] data = newbyte[buffer.remaining()];

        buffer.get(data);

        System.out.println(Thread.currentThread().getName() + " received:"+ newString(data, StandardCharsets.UTF_8));

    }

 

    @Override

    publicvoidfailed(Throwable exc, ByteBuffer buffer) {

 

    }

}

4.2AIO客户端程序

publicclassAioClient {

    publicstaticvoidmain(String[] args) throwsException {

        AsynchronousSocketChannel channel = AsynchronousSocketChannel.open();

        channel.connect(newInetSocketAddress("127.0.0.1", 8080));

        ByteBuffer buffer = ByteBuffer.allocate(1024);

        buffer.put("Java AIO".getBytes(StandardCharsets.UTF_8));

        buffer.flip();

        Thread.sleep(1000L);

        channel.write(buffer);

 }

}

4.3异步的定义猜想结论

分别运行服务端和客户端程序:

在服务端运行结果里:

1)main线程发起serverChannel.accept的调用,添加了一个CompletionHandler监听回调,当有客户端连接过来时,Thread-5线程执行了accep的completed回调方法。

2)紧接着Thread-5又发起了clientChannel.read调用,也添加了个CompletionHandler监听回调,当收到数据时,是Thread-1的执行了read的completed回调方法。

这个结论和上面异步猜想一致:发起IO操作(例如accept、read、write)调用的线程,和最终完成这个操作的线程不是同一个,我们把这种IO模式称之AIO。

当然了,这样定义AIO只是为了方便我们理解,实际中对异步IO的定义可能更抽象一点。

5、 AIO示例引发思考1:“执行completed()方法的线程是谁创建、什么时候创建?”

一般,这样的问题,需要从程序的入口的开始了解,但跟线程相关,其实是可以从线程栈的运行情况来定位线程是怎么运行。

只运行AIO服务端程序,客户端不运行,打印一下线程栈(备注:程序在Linux平台上运行,其他平台略有差异)。如下图所示。

分析线程栈,发现,程序启动了那么几个线程:

  • 1)线程Thread-0阻塞在EPoll.wait()方法上;
  • 2)线程Thread-1、Thread-2~Thread-n(n和CPU核心数量一致)从阻塞队列里take()任务,阻塞等待有任务返回。

此时可以暂定下一个结论:AIO服务端程序启动之后,就开始创建了这些线程,且线程都处于阻塞等待状态。

另外:发现这些线程的运行都跟epoll有关系!

提到epoll,我们印象中,Java NIO在Linux平台底层就是用epoll来实现的,难道Java AIO也是用epoll来实现么?

为了证实这个结论,我们从下一个问题来展开讨论。

6、 AIO示例引发思考2:AIO注册事件监听和执行回调是如何实现的?

带着这个问题,去阅读JDK分析源码时,发现源码特别的长,而源码解析是一项枯燥乏味的过程,很容易把阅读者给逼走劝退掉。

对于长流程和逻辑复杂的代码的理解,我们可以抓住它几个脉络,找出哪几个核心流程。

以注册监听read为例clientChannel.read(...),它主要的核心流程是:注册事件 -> 监听事件 -> 处理事件。

注册事件:

注:注册事件调用EPoll.ctl(...)函数,这个函数在最后的参数用于指定是一次性的,还是永久性。上面代码events | EPOLLONSHOT字面意思看来,是一次性的。

监听事件:

处理事件:

 

核心流程总结:

在分析完上面的代码流程后会发现:每一次IO读写都要经历的这三个事件是一次性的,也就是在处理事件完,本次流程就结束了,如果想继续下一次的IO读写,就得从头开始再来一遍。这样就会存在所谓的死亡回调(回调方法里再添加下一个回调方法),这对于编程的复杂度大大提高了。

7、 AIO示例引发思考3:监听回调的本质是什么?

7.1概述

先说一下结论:所谓监听回调的本质,就是用户态线程调用内核态的函数(准确的说是API,例如read、write、epollWait),该函数还没有返回时,用户线程被阻塞了。当函数返回时,会唤醒阻塞的线程,执行所谓回调函数。

对于这个结论的理解,要先引入几个概念。

7.2系统调用与函数调用

函数调用:找到某个函数,并执行函数里的相关命令。

系统调用:操作系统对用户应用程序提供了编程接口,所谓API。

系统调用执行过程:

  • 1)传递系统调用参数;
  • 2)执行陷入指令,用用户态切换到核心态(这是因为系统调用一般都需要再核心态下执行);
  • 3)执行系统调用程序;
  • 4)返回用户态。

7.3用户态和内核态之间的通信

用户态->内核态:通过系统调用方式即可。

内核态->用户态:内核态根本不知道用户态程序有什么函数,参数是啥,地址在哪里。所以内核是不可能去调用用户态的函数,只能通过发送信号,比如kill 命令关闭程序就是通过发信号让用户程序优雅退出的。

既然内核态是不可能主动去调用用户态的函数,为什么还会有回调呢,只能说这个所谓回调其实就是用户态的自导自演。它既做了监听,又做了执行回调函数。

7.4用实际例子验证结论

为了验证这个结论是否有说服力,举个例子:平时开发写代码用的IntelliJ IDEA,它是如何监听鼠标、键盘事件和处理事件的。

按照惯例,先打印一下线程栈,会发现鼠标、键盘等事件的监听是由“AWT-XAWT”线程负责的,处理事件则是“AWT-EventQueue”线程负责。如下图所示。

定位到具体的代码上:可以看到“AWT-XAWT”正在做while循环,调用waitForEvents函数等待事件返回。如果没有事件,线程就一直阻塞在那边。如下图所示。

8、Java AIO的本质是什么?

8.1Java AIO的本质,就是只在用户态实现了异步

由于内核态无法直接调用用户态函数,Java AIO的本质,就是只在用户态实现异步,并没有达到理想意义上的异步。

1)理想中的异步:

何谓理想意义上的异步?这里举个网购的例子。

两个角色,消费者A、快递员B:

  • 1)A在网上购物时,填好家庭地址付款提交订单,这个相当于注册监听事件;
  • 2)商家发货,B把东西送到A家门口,这个相当于回调。

A在网上下完单,后续的发货流程就不用他来操心了,可以继续做其他事。B送货也不关心A在不在家,反正就把货扔到家门口就行了,两个人互不依赖,互不相干扰。

假设A购物是用户态来做,B送快递是内核态来做,这种程序运行方式过于理想了,实际中实现不了。

2)现实中的异步:

A住的是高档小区,不能随意进去,快递只能送到小区门口。

A买了一件比较重的商品,比如一台电视,因为A要上班不在家里,所以找了一个好友C帮忙把电视搬到他家。

A出门上班前,跟门口的保安D打声招呼,说今天有一台电视送过来,送到小区门口时,请电话联系C,让他过来拿。

具体就是:

  • 1)此时,A下单并跟D打招呼,相当于注册事件。在AIO中就是EPoll.ctl(...)注册事件;
  • 2)保安在门口蹲着相当于监听事件,在AIO中就是Thread-0线程,做EPoll.wait(..);
  • 3)快递员把电视送到门口,相当于有IO事件到达;
  • 4)保安通知C电视到了,C过来搬电视,相当于处理事件(在AIO中就是Thread-0往任务队列提交任务,Thread-1 ~n去取数据,并执行回调方法)。

整个过程中,保安D必须一直蹲着,寸步不能离开,否则电视送到门口,就被人偷了。

好友C也必须在A家待着,受人委托,东西到了,人却不在现场,这有点失信于人。

所以实际的异步和理想中的异步,在互不依赖,互不干扰,这两点相违背了。保安的作用最大,这是他人生的高光时刻。

异步过程中的注册事件、监听事件、处理事件,还有开启多线程,这些过程的发起者全是用户态一手操办。所以说Java AIO本质只是在用户态实现了异步,这个和BIO、NIO先阻塞,阻塞唤醒后开启异步线程处理的本质一致。

8.2Java AIO的其它真相

Java AIO跟NIO一样:在各个平台的底层实现方式也不同,在Linux是用epoll、Windows是IOCP、Mac OS是KQueue。原理是大同小异,都是需要一个用户线程阻塞等待IO事件,一个线程池从队列里处理事件。

Netty之所以移除掉AIO:很大的原因是在性能上AIO并没有比NIO高。Linux虽然也有一套原生的AIO实现(类似Windows上的IOCP),但Java AIO在Linux并没有采用,而是用epoll来实现。

Java AIO不支持UDP。

AIO编程方式略显复杂,比如“死亡回调”。

9、参考资料

[1] 少啰嗦!一分钟带你读懂Java的NIO和经典IO的区别

[2] 史上最强Java NIO入门:担心从入门到放弃的,请读这篇!

[3] Java的BIO和NIO很难懂?用代码实践给你看,再不懂我转行!

[4] Java新一代网络编程模型AIO原理及Linux系统AIO介绍

[5] 从0到1的快速裂变:详解快的打车架构设计及技术实践

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

[7] 史上最通俗Netty框架入门长文:基本介绍、环境搭建、动手实战

[8] 高性能网络编程(五):一文读懂高性能网络编程中的I/O模型

[9] 高性能网络编程(六):一文读懂高性能网络编程中的线程模型

[10] 高性能网络编程(七):到底什么是高并发?一文即懂!

[11] 从根上理解高性能、高并发(二):深入操作系统,理解I/O与零拷贝技术

[12] 从根上理解高性能、高并发(三):深入操作系统,彻底理解I/O多路复用

[13] 从根上理解高性能、高并发(四):深入操作系统,彻底理解同步与异步

[14] 从根上理解高性能、高并发(五):深入操作系统,理解高并发中的协程

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

posted @ 2023-06-21 11:19 Jack Jiang 阅读(244) | 评论 (0)编辑 收藏

为了更好地分类阅读 52im.net 总计1000多篇精编文章,我将在每周三推送新的一期技术文集,本次是第17 期。

[- 1 -] 社交软件红包技术解密(一):全面解密QQ红包技术方案——架构、技术实现等

[链接] http://www.52im.net/thread-2202-1-1.html

[摘要] 本文将从架构开始,到手机 QQ 移动端优化,再到个性化红包和 AR 新玩法,为大家全面解密 QQ 红包技术方案。


[- 2 -] 社交软件红包技术解密(二):解密微信摇一摇红包从0到1的技术演进

[链接http://www.52im.net/thread-2519-1-1.html

[摘要今天下午跟大家分享的主题是:微信团队是如何从0到1实现“有把握”的微信春晚摇一摇红包系统的。


[- 3 -] 社交软件红包技术解密(三):微信摇一摇红包雨背后的技术细节

[链接http://www.52im.net/thread-2533-1-1.html

[摘要本文将由微信团队工程师张文瑞分享微信春节摇一摇红包技术背后的方方面面,希望能给同行们带来启发。


[- 4 -]社交软件红包技术解密(四):微信红包系统是如何应对高并发的

[链接] http://www.52im.net/thread-2548-1-1.html

[摘要本文将为读者介绍微信百亿级别红包背后的高并发设计实践,内容包括微信红包系统的技术难点、解决高并发问题通常使用的方案,以及微信红包系统的所采用高并发解决方案。


[- 5 -] 社交软件红包技术解密(五):微信红包系统是如何实现高可用性的

[链接http://www.52im.net/thread-2564-1-1.html

[摘要本次分享介绍了微信红包后台系统的高可用实践经验,主要包括后台的 set 化设计、异步化设计、订单异地存储设计、存储层容灾设计与平行扩缩容等。听众可以了解到微信红包后台架构的设计细节,共同探讨高可用设计实践上遇到的问题与解决方案。


[- 6 -] 社交软件红包技术解密(六):微信红包系统的存储层架构演进实践

[链接http://www.52im.net/thread-2568-1-1.html

[摘要] 微信红包本质是小额资金在用户帐户流转,有发、抢、拆三大步骤。在这个过程中对事务有高要求,所以订单最终要基于传统的RDBMS,这方面是它的强项,最终订单的存储使用互联网行业最通用的MySQL数据库。支持事务、成熟稳定,我们的团队在MySQL上有长期技术积累。但是传统数据库的扩展性有局限,需要通过架构解决。


[- 7 -] 社交软件红包技术解密(七):支付宝红包的海量高并发技术实践

[链接http://www.52im.net/thread-2573-1-1.html

[摘要本文将为读者剖析支付宝红包系统背后的技术细节。


[- 8 -]  社交软件红包技术解密(八):全面解密微博红包技术方案

[链接http://www.52im.net/thread-2576-1-1.html

[摘要在服务器数量一定的情况下,如何构建高并发操作、瞬间峰值高的稳定服务?对于团队和架构师都是一个极大的挑战。这时候系统的架构尤为重要!本文将为你分享这些内容。


[- 9 -] 社交软件红包技术解密(九):谈谈手Q红包的功能逻辑、容灾、运维、架构等

[链接http://www.52im.net/thread-2583-1-1.html

[摘要本文将会详细介绍手Q春节红包项目的功能设计/逻辑、容灾、运维、架构以及实践总结。


[- 10 -] 社交软件红包技术解密(十):手Q客户端针对2020年春节红包的技术实践

[链接] http://www.52im.net/thread-2966-1-1.html

[摘要对于这种大体量的IM社交应用运营活动,技术上除了前端、后台的大力支撑,对于手Q客户端来说,又是从哪些方面来保证整个红包活动的灵活性、稳定性和用户体验的呢?带着这个问题,我们一起来阅读余下的文字。


[- 11 -] 社交软件红包技术解密(十一):解密微信红包随机算法(含代码实现)

[链接] http://www.52im.net/thread-3125-1-1.html

[摘要本文根据有限的资料,分享了微信红包随机算法实现中的一些技术要点,并整理了两种比较靠谱的红包算法实现思路(含可运行的实现代码),希望能给你的红包算法开发带来启发。


[- 12 -] 社交软件红包技术解密(十二):解密抖音春节红包背后的技术设计与实践

[链接http://www.52im.net/thread-3945-1-1.html

[摘要本文将要分享的是春节期间海量红包社交活动为抖音所带来的各种技术挑战,以及抖音技术团队是如何在实践中一一解决这些问题的。


👉52im社区本周新文:《到底什么是Java AIO?为什么Netty会移除AOI?一文搞懂AIO的本质!http://www.52im.net/thread-4283-1-1.html》,欢迎阅读!👈

我是Jack Jiang,我为自已带盐!https://github.com/JackJiang2011/MobileIMSDK/

posted @ 2023-06-19 13:40 Jack Jiang 阅读(86) | 评论 (0)编辑 收藏

► 相关链接:

一、技术准备

您是否已对Web端即时通讯技术有所了解?

您需要对WebSocket技术有所了解:

WebSocket标准文档、API手册:

二、开发工具准备

1)WebStorm:

(JackJiang 使用的版本号如上图所示,建议你也使用此版或较新版本)

2)一站式下载地址:WebStorm官方下载地址点此进入

三、工程文件用途说明

3.1文件概览

纯原生JS实现,无任何重框架依赖:

MobileIMSDK-H5端SDK本身只是JS文件源码的集合,本工程中自带的前端Demo的目的只是为了方便随时测试MobileIMSDK-H5端的SDK代码而已,在此工程中的使用也仅仅只涉及了一个主Demo页面而已。

工程目录说明:

3.2详细说明

SDK 各模块/文件作用说明:

四、主要 API 接口

4.1主要 API 接口概览

如下图所示:所有 SDK 接口均由/mobileimsdk/mobileimsdk-client-sdk.js 提供。,接口设计跟MobileIMSDK 的APP版一样,均为高内聚和低侵入的回调方式传入SDK处理逻辑,无需(也不建议)开发者直接修改sdk级代码。

▲ 图上为浏览器端SDK的对外接口文件位置

▲ 图上为浏览器SDK为开发者提供的回调接口

▲ 图上浏览器端SDK的对外接口文件全图

4.2主要 API 接口用途说明

1)IMSDK.isLogined():

  • 用途:是否已经完成过首次登陆。
  • 说明 :用户一旦从自已的应用中完成登陆IM服务器后,本方法就会一直返回true(直到退出登陆IM)。
  • 返回值:{boolean},true表示已完成首次成功登陆(即已经成功登陆过IM服务端了,后面掉线时不影响此标识),否则表示尚未连接IM服务器。

2)IMSDK.isOnline():

  • 用途:是否在线。
  • 说明 :表示网络连接是否正常。
  • 返回值:{boolean},true表示网络连接正常,否则表示已掉线,本字段只在this._logined=true时有意义(如果都没有登陆到IM服务器,怎么存在在线或掉线的概念呢)。

3)IMSDK.getLoginInfo():

  • 用途:返回登陆时提交的登陆信息(用户名、密码/token等)。
  • 说明 :格式形如:{loginUserId:'',loginToken:''},此返回值的内容由调用登陆函数 loginImpl()时传入的内容决定。字段定义详见:PLoginInfo
  • 返回值:{boolean},true表示网络连接正常,否则表示已掉线,本字段只在this._logined=true时有意义(如果都没有登陆到IM服务器,怎么存在在线或掉线的概念呢)。

4)IMSDK.sendData(p, fnSucess, fnFail, fnComplete):

  • 用途:向某人发送一条消息。
  • 参数p:{Protocal} 要发送的消息协议包对象,Protocal详情请见“/module/mb_constants.js”下的createCommonData函数说明。
  • 返回值:{int} 0表示成功,否则表示错误码,错码详见“/module/mb_constants.js”下的MBErrorCode对象属性说明。

5)IMSDK.disconnectSocket():

  • 用途:客户端主动断开客户端socket连接。
  • 说明 :当开发者登陆IM后,需要退出登陆时,调用本函数就对了,本函数相当于登陆函数 loginImpl()的逆操作。

6)IMSDK.setDebugCoreEnable(enable):

  • 用途:是否开启MobileIMSDK-Uniapp端核心算法层的log输入,方便开发者调试。
  • 参数enable :{boolean} true表示开启log输出,否则不输出,开发者不调用本函数的话系统默认是false(即不输出log)。

7)IMSDK.setDebugSDKEnable(enable):

  • 用途:是否开启MobileIMSDK-Uniapp端框架层的log输入,方便开发者调试。
  • 参数enable :{boolean} true表示开启log输出,否则不输出,开发者不调用本函数的话系统默认是false(即不输出log)。

8)IMSDK.setDebugPingPongEnable(enable):

  • 用途:是否开启MobileIMSDK-Uniapp端框架层的底层网络WebSocket心跳包的log输出,方便开发者调试。
  • 参数enable :{boolean} true表示开启log输出,否则不输出,开发者不调用本函数的话系统默认是false(即不输出log)。
  • 注意:必须 setDebugEnable(true) 且 setDebugPingPongEnable(true) 时,心跳log才会真正输出,方便控制。
  • 返回值:true表示开启log输出,否则不输出,开发者不调用本函数的话系统默认是false(即不输出log)。

9)IMSDK.loginImpl(varloginInfo, wsUrl):

  • 用途:登陆/连接MobileIMSDK服务器时调用的方法。
  • 说明 :登陆/连接MobileIMSDK服务器由本函数发起
  • 参数varloginInfo:{PLoginInfo} 必填项,登陆要提交给Websocket服务器的认证信息,不可为空,对象字段定义见:PLoginInfo
  • 参数wsUrl:{string} 必填项:要连接的Websocket服务器地址,不可为空,形如:wss://yousite.net:3000/websocket。

10)IMSDK.callback_onIMLog(message, toConsole):

  • 用途:由开发者设置的回调方法:用于debug的log输出。
  • 推荐用法 :开发者可在此回调中按照自已的意图打印MobileIMSDK微信小程序端框架中的log,方便调试时使用。
  • 参数1: {String}:必填项,字符串类型,表示log内容。
  • 参数2: {boolean}:选填项,true表示输出到console,否则默认方式(由开发者设置的回调决定)。

11)IMSDK.callback_onIMData(p, options):

  • 用途:由开发者设置的回调方法:用于收到聊天消息时在UI上展现出来(事件通知于收到IM消息时)。
  • 推荐用法:开发者可在此回调中处理收到的各种IM消息。
  • 参数1: {Protocal}:详情请见“/module/mb_constants.js”下的Protocal类定义)。

12)IMSDK.callback_onIMAfterLoginSucess():

  • 用途:由开发者设置的回调方法:客户端的登陆请求被服务端成功认证完成后的回调(事件通知于 登陆/认证 成功后)。
  • 推荐用法 :开发者可在此回调中进行登陆IM服务器成功后的处理。

13)IMSDK.callback_onIMAfterLoginFailed(isReconnect):

  • 用途:由开发者设置的回调方法:客户端的登陆请求被服务端认证失败后的回调(事件通知于 登陆/认证 失败后)。
  • 说明 :补充说明:登陆/认证失败的原因可能是用户名、密码等不正确等,但具体逻辑由服务端的 callBack_checkAuthToken回调函数去处理。
  • 推荐用法:开发者可在此回调中提示用户登陆IM服务器失败。。
  • 参数1: {boolean}:true表示是掉线重连后的认证失败(在登陆其间可能用户的密码信息等发生了变更),否则表示首次登陆时的认证失败。

14)IMSDK.callback_onIMReconnectSucess():

  • 用途:由开发者设置的回调方法:掉线重连成功后的回调(事件通知于掉线重连成功后)。
  • 推荐用法 :开发者可在此回调中处理掉线重连成功后的界面状态更新等,比如设置将界面上的“离线”文字更新成“在线”。

15)IMSDK.callback_onIMDisconnected():

  • 用途:由开发者设置的回调方法:网络连接已断开时的回调(事件通知于与服务器的网络断开后)。
  • 推荐用法 :开发者可在此回调中处理掉线时的界面状态更新等,比如设置将界面上的“在线”文字更新成“离线”。

16)IMSDK.callback_onIMPing():

  • 用途:由开发者设置的回调方法:本地发出心跳包后的回调通知(本回调并非MobileIMSDK-Uniapp端核心逻辑,开发者可以不需要实现!)。
  • 推荐用法 :开发者可在此回调中处理底层网络的活动情况。

17)IMSDK.callback_onIMPong():

  • 用途:由开发者设置的回调方法:收到服务端的心跳包反馈的回调通知(本回调并非MobileIMSDK-Uniapp端核心逻辑,开发者可以不需要实现!)。
  • 推荐用法 :开发者可在此回调中处理底层网络的活动情况。

18)IMSDK.callback_onIMShowAlert(alertContent):

  • 用途:由开发者设置的回调方法:框架层的一些提示信息显示回调(本回调并非MobileIMSDK-Uniapp端核心逻辑,开发者可以不需要实现!)。
  • 说明 :开发者不设置的情况下,框架默认将调用wx.showModal()显示提示信息,否则将使用开发者设置的回调——目的主要是给开发者自定义这种信息的UI显示,提升UI体验,别无它用】。
  • 参数1:{String}:必填项,文本类型,表示提示内容。

19)IMSDK.callback_onIMKickout(kickoutInfo):

  • 用途:由开发者设置的回调方法:收到服务端的“踢出”指令(本回调并非MobileIMSDK-Uniapp端核心逻辑,开发者可以不需要实现!)。
  • 参数1 :{PKickoutInfo}:非空,详见:PKickoutInfo

20)IMSDK.callback_onMessagesLost(lostMessages):

  • 用途:由开发者设置的回调方法:消息未送达的回调事件通知。
  • 发生场景 :比如用户刚发完消息但网络已经断掉了的情况下,表现形式如:就像手机qq或微信一样消息气泡边上会出现红色图标以示没有发送成功)。
  • 建议用途:应用层可通过回调中的指纹特征码找到原消息并可以UI上将其标记为“发送失败”以便即时告之用户。
  • 参数1:{Array<rotocal>}:由框架的QoS算法判定出来的未送达消息列表。

21)IMSDK.callback_onMessagesBeReceived(theFingerPrint):

  • 用途:由开发者设置的回调方法:消息已被对方收到的回调事件通知。
  • 说明 :目前,判定消息被对方收到是有两种可能:
  • 1) 对方确实是在线并且实时收到了;
  • 2) 对方不在线或者服务端转发过程中出错了,由服务端进行离线存储成功后的反馈(此种情况严格来讲不能算是“已被收到”,但对于应用层来说,离线存储了的消息原则上就是已送达了的消息:因为用户下次登陆时肯定能通过HTTP协议取到)。
  • 建议用途:应用层可通过回调中的指纹特征码找到原消息并可以UI上将其标记为“发送成功”以便即时告之用户。
  • 参数1:{String}:已被收到的消息的指纹特征码(唯一ID),应用层可据此ID找到原先已发的消息并可在UI是将其标记为”已送达“或”已读“以便提升用户体验。

五、前端开发指南

5.1如何引入SDK文件到您的前端工程中?

很简单:只需要将第2节中提到的SDK所有JS文件复制到您的Uniapp工程下即可。

SDK内容见下图:

5.2如何在代码中调用SDK?

第一步:在你的网页中引用SDK的js文件(具体例子详见Demo中的index.html文件)

第二步:直接在你的JS文件中编写回调配置代码具体例子详见Demo中的index.js文件

第三步:在你的JS文件中调用IM的登陆方法即可具体例子详见Demo中的index.js文件

注意:上图中登录连接的IP地址请设置为您的MobileIMSDK服务器地址哦。

六、Demo运行方法(在WebStorm中直接预览)

6.1重要说明

特别说明:MobileIMSDK的H5端(包括Demo在内),全部是静态的HTML+JS资源,可以通过WebStorm自带的HTML页面预览功能,直接自动加载到电脑的浏览器中运行和预览。

6.2预览方法

1)在Demo中的index.html文件中,移动鼠标,会在右上角出现如下图所示的浮出菜单:

2)点击右上角浮出菜单上相应的浏览器就可以自动预览了这里以我电脑上已安装的Edge浏览器为例):

七、Demo运行方法(在Web服务器中部署并访问)

7.1重要说明

特别说明:MobileIMSDK的H5端(包括Demo在内),全部是静态的HTML+JS资源,对于服务端是没有任何依赖的,只需要保证浏览器端能加载到即可,可以把它们放置在Tomcat、Apache、IIS、Nginx等等传统Web服务器中即可,无需任何动态运行环境。

7.2安装Tomcat

提示:以下Demo的部署,以Java程序员最常用和Tomcat为例(Apache、IIS、Nginx等依此类推)。

Tomcat的安装就没什么好说的,直接官网下载对应的版本即可:https://tomcat.apache.org/download-90.cgi

7.3配置要连接的MobileIMSDK服务器IP

注意:下图中登陆连接的IP地址请设置为您的MobileIMSDK服务器地址哦。

友情提示: MobileIMSDK的服务端该怎么部署就不是本手册要讨论的内容了,你可以参见《即时通讯框架MobileIMSDK的Demo使用帮助:Server端》。

▲ 配置要连接的服务器IP以上代码详见demo/index.js 文件

7.4部署Demo

说“部署”有点扯蛋,因为Demo(包括SDK)在内,全是HTML静态内容,只需要直接复制到任何一种Web服务器即可。

以下是复制到Tomcat服务器网页目录后的截图:

7.5启动Tomcat

提示:本手册中仅以启Tomcat为例,Apache、IIS、Nginx等Web服务器的启动请自动百度。

运行startup.bat启动Tomcat:

7.6Demo的运行效果预览

八、Demo功能预览和说明

九、Demo运行效果实拍图

1)Demo在手机端浏览器中的真机实拍图:

2)Demo在电脑端浏览器中的真机实拍图:

十、更多Demo运行效果截图

1)Demo在PC端浏览器运行效果:

2)Demo在手机端浏览器运行效果:

 

3)Demo在PC端各主流浏览器的运行效果:

十一、常见问题(FAQ)

11.1为什么浏览控制台下有些log不显示?

原因是浏览器控制台下的日志级别默认进行了过滤,勾选所有日志级别,就能看到SDK的详细日志输出了。

勾选所有的日志输出级别:

然后就能看到SDK中详细的日志输出了(就像下图这样),方便调试和研究:

十二、引用资料

[1] WebSocket 标准API手册

[2] MobileIMSDK开源框架的API文档

[3] MobileIMSDK开源IM框架源码Github地址点此

[4] MobileIMSDK-H5端基本介绍

[5] MobileIMSDK-H5端的开发手册(* 精编PDF版)

[6] MobileIMSDK的Demo使用帮助:Server端

[7] WebSocket从入门到精通,半小时就够!

posted @ 2023-06-15 11:55 Jack Jiang 阅读(107) | 评论 (0)编辑 收藏

一、关于RainbowChat-Web

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

二、v5.0 版更新内容

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

  • 1)[bug][前端]     - 解决了当首页“消息”无item时,从好友列表中删除某人时不自动清空聊天面板和右侧详情面板的问题;
  • 2)[bug][前端]     - 解决了处于群列表Tab时,退群或解散群不会更新群列表中“当前群聊”数字的问题 ;
  • 3)[bug][前端]     - 解决了处于群列表Tab时,点击创建群聊后,不会在群聊列表中自动选中此创建的群的问题;
  • 4)[优化]             - 升级核心通信层框架MobileIMSDK-Web至最新v5.1版;
  • 5)[优化][前端]    - 优化了当发送名片消息时,如名片者未设置头像,则在聊天消息界面中显示默认头像(提升体验);
  • 6)[优化][服务端] - 进一步加固了uid登陆时的sql注入风险;
  • 7)[优化][服务端] - 解决与最新rabbitmq-client库不兼容从而断线重连不成功,导致MQ中消息堆积的问题:
  • 8)[优化][服务端] - 解决MQ断线自动恢复时消费者Chennal未主动清理,导致空channel越来越多的问题;
  • 9)[优化][前端]    - 解决了被踢出群的情况下,仍能退群、邀请别人入群等问题;
  • 10)[优化][前端]  - 解决了高版本Tomcat下文件名中包含了特殊符号的大文件无法下载的问题。
  • 11)[新增][前端]  - 聊天区上方实现了聊天对象信息的显示(可显示昵称、群名称等信息);
  • 12)[新增][前端]  - 新增了消息送达状态图标的显示(包括发送中、发送成功、发送失败3种状态)。

三、v5.0 版新增特性截图

聊天区上方聊天对象信息的演示运行截图更多运行截图):

消息送达状态的演示运行截图更多运行截图):

四、主要界面截图概览

 ▲ 主界面(更多截图更多演示视频

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

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

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

仅列出标题
共43页: First 上一页 3 4 5 6 7 8 9 10 11 下一页 Last 
Jack Jiang的 Mail: jb2011@163.com, 联系QQ: 413980957, 微信: hellojackjiang