浏览 OpenIM Guides
Guides

性能与可靠性报告

使用测试程序模拟大量用户在线与消息收发,评估容量与链路稳定性。

复制

背景

为了全面测试 OpenIMSDK,首先需要明确其核心功能和架构。OpenIMSDK 不是一个独立的聊天应用程序,如 WeChat 或 Slack,而是一个包含 IMSDK(用于客户端集成)和 IMServer(用于私有化部署)的开源即时通讯解决方案。这使得开发者可以在自己的应用程序中轻松集成即时通讯功能,提供一个替代 Twilio 或 Sendbird 等即时通讯云服务的选择。由于 IMSDK 底层是用 Go 语言编写的 openim-sdk-core,模拟大规模用户在移动设备上的使用场景具有一定的挑战。实际上,使用成千上万台手机进行测试是不现实的。为此,我们设计了两套测试程序:

  1. 测试程序 A-可靠性
  2. 该程序在服务器上加载并运行 openim-sdk-core 来模拟 IMSDK 实例。每个实例都使用 SQLite 进行消息存储。这种方法可以全面模拟客户端的行为,包括消息的发送、接收、存储和回调,有效验证消息收发的可靠性和时延。但是,如果在单个服务器上运行过多的 SDK 实例,可能会导致客户端 IMSDK 性能下降。
  3. 测试程序 B-压力
  4. 此程序精简了 IMSDK 的功能,只保留登录和消息收发功能,旨在通过启动大量实例模拟高并发场景,以评估服务器在重负载下的表现和稳定性。虽然这种方法不能完全验证客户端 SDK 的全部流程,但它对于评估服务器的承载能力和资源管理策略至关重要。它的缺点是:不能完整模拟 IMSDK 的功能,也就无法验证消息收发可靠性。

综合这两种测试程序,程序 B 主要用于模拟大量用户同时在线并进行消息交互,以增加服务器的负荷;而程序 A 则用于加载 IMSDK,通过抽样统计来评估消息的可靠性和时延。两种程序的联合使用可以更好地模拟真实用户场景,并客观地提供 IMSDK 的测试报告。这种双重测试策略确保了从不同角度对系统进行全面的评估和验证。

可靠性和时延测试

消息的可靠性通常指的是消息投递的可靠性,即所谓的“消息必达”。这意味着一旦消息被发送,它必须能够被接收者成功收到。考虑到网络环境的复杂性和用户在线状态的不确定性,消息的可靠性无疑成为 IM 系统的一个核心性能指标,也是实现上的一大挑战。通常所说的 IM 系统的“可靠性”,主要是指聊天消息的投递可靠性。需要说明的是,这里的“消息”是广义的,包括用户看不见的各种指令和通知,如进群退群通知、好友添加通知等。

消息时延则指从客户端 A 创建并发送消息,到客户端 B 成功接收并入库这一消息的耗时。有些 IM 系统仅将消息从客户端发送到服务器的接收计入消息时延的统计,这种做法并不全面。正确的做法应该包括从客户端 A 发送到客户端 B 接收的整体耗时。

测试资源

服务器 1:Ubuntu 22.04.2,16Core,64GB RAM,150GB 机械磁盘:部署组件和 IMServer;同时部署测试程序 B,可能也会部署测试程序 A 在共享内存/dev/shm 上

服务器 2:Ubuntu 18.04.5,4Core,8GB RAM,40GB 机械磁盘:部署测试程序 A 在共享内存/dev/shm 上

在服务端 start-config.yml 中,把服务的实例调整为 openim-push: 8,openim-msgtransfer: 8,其他保持 1 个实例不变。

测试场景和结果

测试一:200 个用户

测试程序 A,模拟 200 个用户,其中 100 个立刻登录,向小群、好友发送消息

测试程序 A 统计消息完整性和时延:go run main.go -lgr 0.5 -imf -crg -ckgn -ckcon -sem -ckmsn -u 200 -su 10 -lg 2 -cg 2 -cgm 5 -sm 5 -gm 5 -reg

此时,测试程序 A 部署在服务器 1 的共享内存/dev/shm 上

参数/结果描述
测试目的少量用户情况下测试消息可靠性和耗时
用户数量200 用户,其中 100 人立刻登录,另外 100 个延迟登录
群数量及规模每人加入 0-10 个常规群,群成员人数为 5 人
发消息频率峰值 40 条/s
消息总数112350
消息完整性100%(所有消息精准送达)
消息平均时延0.231 秒
消息最大时延1.703 秒

测试二:5 万在线用户+小群

测试程序 B:模拟 5 万在线用户,随机发送消息

测试程序 A:模拟 100 个用户,其中 80 个立刻登录,向小群、好友发送消息

测试程序 A 注册 10 万用户:go run main.go -reg -u 100000

测试程序 B 启动 5 万在线用户:go run main.go -s 49500 -e 99500 -c 100 -i 500 -rs 1000 -rr 1000

测试程序 A 统计消息完整性和时延: go run main.go -lgr 0.8 -imf -crg -ckgn -ckcon -sem -ckmsn -u 100 -su 3 -lg 0 -cg 4 -cgm 5 -sm 100 -gm 100 -msgitv 1500 -test

此时,测试程序 A 部署在服务器 2 的共享内存/dev/shm 上,测试程序 B 部署在服务器 1 上。

测试二数据量较大,跑完需要大约 4 小时。如果希望更快出结果可以调小测试数据量。
参数/结果描述
压力情况50000 用户在线,每秒发送约 1700 条消息
抽样统计用户数100 用户,其中 80 人立刻登录,另外 20 个延迟登录
抽样统计用户的群数量及规模每人加入 0-20 个常规群,群成员人数为 5 人
抽样统计消息发送频率峰值 54 条/s
抽样统计消息数170800
抽样统计消息完整性100%(所有消息精准送达)
抽样统计消息平均时延0.202 秒
抽样统计消息最大时延3.641 秒

测试三:5 万在线用户+5 万大群

测试程序 B:模拟 5 万在线用户,随机发送消息

测试程序 A:模拟 20 个用户,其中 16 个立刻登录,向 10 个 5 万人群聊、好友发送消息

测试程序 A 注册 10 万用户:go run main.go -reg -u 100000

测试程序 B 启动 5 万在线用户:go run main.go -o 50000 -s 49500 -e 99500 -c 100 -i 500 -rs 1000 -rr 1000

测试程序 A 统计消息完整性和时延:go run main.go -lgr 0.8 -imf -crg -ckgn -ckcon -sem -ckmsn -u 20 -su 3 -lg 10 -cg 0 -cgm 5 -sm 0 -gm 10

此时,测试程序 A 部署在服务器 2 的共享内存/dev/shm 上,测试程序 B 部署在服务器 1 上

参数/结果描述
压力情况50000 用户在线,每秒发送约 1700 条消息
抽样统计用户数20 用户,其中 16 人立刻登录,另外 4 个延迟登录
抽样统计用户的群数量及规模10 个大群,每个群 50000 人,其中 500 人在线
抽样统计消息发送频率峰值 32 条/s
抽样统计消息数24000
抽样统计消息完整性100%(所有消息精准送达)
抽样统计消息平均时延0.022 秒
抽样统计消息最大时延1.664 秒

测试二服务器资源消耗

登录用户数:

br-login

消息压力:(下图指标表示 1 分钟服务器接收到的消息量)

br-msg

CPU 使用情况:

br-cpu

进程CPU 占用
openim-msggateway210%
mongo100%
kafka84%
redis67%
openim-rpc-msg56%
openim-msgtransfer27%*8
openim-push13%*8
其他OpenIM服务以及组件65%
总计902%

物理内存占用情况:

br-mem

进程内存占用
openim-msggateway2.1 GiB
mongo717 MiB
kafka1.1GiB
redis85MiB
openim-rpc-msg162MiB
openim-msgtransfer74MiB*8
openim-push126MiB*8
其他OpenIM服务以及组件457MiB
总计(所有OpenIM服务以及组件)6.986GiB

*备注:以上表格内容是粗略统计,在总计中不包括 docker 转发数据到容器的资源消耗,仅供参考。*

结果分析

OpenIM 支持同时在线 5 万人,能够处理多个 5 万人超级大群,面对每秒 1700 条消息的压力时,消息可达率达到 100%。平均消息时延低于 1 秒,最大时延不超过 3 秒,展现出卓越的性能和可靠性。

配置建议

按照 10 万注册用户,日常在线 10%用户,支持 5 万大群计算,每秒 600 条消息,建议配置:

名称配置
内存16G
CPU8 核
网络带宽10M

*备注:消息包大小按照 2KB 计算。实际消息包大小根据发送内容而异,常规的一句文字消息消息包大小为 700 字节左右。*

压测程序运行说明

本次测试用到了两个测试程序:

压力测试程序,路径:openim-sdk-core/msgtest/

可靠性测试程序,路径:openim-sdk-core/integration_test/

每一次运行压测程序,需要确保服务端和客户端初始数据都为空。服务端需要删除components文件夹并重启docker组件,客户端需要清楚数据库文件(默认在integration_test/datadata文件夹需要手动创建)。

以下是可靠性测试程序的使用说明以及检测逻辑的说明。

参数说明

测试程序支持通过配置参数来指定不同的测试场景。通过灵活设置参数,用户可以自由地模拟各种复杂场景,涵盖不同的网络状态和操作流程,从而更精确地评估消息通道的可靠性。

参数含义类型
testsdk 是否运行测试模式,测试模式的 sdk 对于大量消息有更高容忍度bool
u用户数量int
su拥有所有好友的用户数量int
lg包含大群的数量int
lgm大群人数int
cg每个人创建的常规群聊数量int
cgm常规群人数int
sm每人发送的私聊消息数量int
gm每人发送的群聊消息数量int
msgitv发送消息间隔(单位:ms),群聊消息和单聊消息独立计算间隔int
reg是否注册bool
imf是否导入好友bool
crg是否创建群组bool
sem是否发送消息bool
ckgn是否检查群组数量bool
ckcon是否检查会话数量bool
ckmsn是否检查消息数量bool
ckuni是否模拟卸载和重新安装并再次检查bool
lgr登录用户比例/登录率float

以下为一个运行命令的示例:

go run main.go -test -u 10 -su 3 -lg 2 -cg 4 -cgm 5 -sm 6 -gm 7 -msgitv 1000 -reg -lgr 0.7 -imf -crg -ckgn -ckcon -sem -ckmsn -ckuni

该命令的参数含义如下:

  • -test:运行测试模式
  • -u 10:总共创建 10 个用户
  • -su 3:创建 3 个超级用户
  • -lg 2:创建 2 个大群聊
  • -cg 4:每个登录用户创建 4 个小群聊
  • -cgm 5:每个小群聊包含 5 个成员
  • -sm 6:发送 6 条私聊消息
  • -gm 7:发送 7 条群聊消息
  • -msgitv 1000:每隔 1000ms 发送一条消息
  • -reg:执行用户注册
  • -lgr 0.7:70%的用户登录
  • -imf:导入好友
  • -crg:创建群聊
  • -ckgn:检测群聊数量
  • -ckcon:检测会话数量
  • -sem:发送消息
  • -ckmsn:检测消息数量
  • -ckuni:模拟卸载重装操作

配置文件说明

配置文件位于 internal/config/ 目录中,基础配置文件为 config.go,配置如下:

	TestIP              = "127.0.0.1"  // ip 地址
	APIAddr             = "http://" + TestIP + ":10002"
	WsAddr              = "ws://" + TestIP + ":10001"
	AdminUserID         = "imAdmin"    // 服务端管理员ID
	Secret              = "openIM123"  // 服务端管理员密码
	PlatformID          = constant.WindowsPlatformID // 模拟的登录平台类型
	LogLevel            = 3  // 日志级别
	DataDir             = "./data/"  // 数据文件路径
	LogFilePath         = "./logs/"  // 日志文件路径
	IsLogStandardOutput = false    // 是否在控制条输出日志,建议false

具体实现方案

测试程序的核心操作可以分为两类:模拟操作检测操作。模拟操作用于模拟实际场景中的用户行为,如注册、登录、消息发送等。由于模拟操作涉及异步执行,为确保所有操作正常完成,测试程序会执行检测操作,以验证结果的正确性。例如,在登录过程中,必须确保每个客户端成功连接至服务器,才能继续后续操作,此时进行用户登录数量的检测尤为重要。

模拟操作说明

模拟操作逻辑位于 internal/manager 目录下,目前包含以下几项核心操作:

用户注册

通过调用服务端 API 来模拟用户注册。用户分为超级用户普通用户。超级用户拥有所有用户作为好友,普通用户仅添加超级用户为好友。

  • 假设系统中存在 u 个用户,其中 su 个为超级用户,则用户的编号范围为 0~u-1。编号为 0~su-1 的用户为超级用户,剩余用户为普通用户。

用户登录

用户登录的数量通过计算 用户总数 * 登录率,并向下取整得到。被选中的用户编号范围为 [0, LoginUserNum)

好友导入

好友导入操作同样通过调用服务端 API 模拟完成。每个超级用户会向其编号之后的所有用户发送好友申请,服务端自动同意申请并发送通知。

群聊创建

群聊分为大群聊常规群聊

  • 大群聊:大群包含所有用户,创建数量由参数 gl 指定。创建者按编号从 0 开始轮流分配,超出登录用户人数时重新从编号 0 开始。
  • 常规群聊:每个登录用户都会创建常规群聊,创建数量由参数 gs 指定,群成员数量由 gms 参数控制。用户编号为 n 的用户会选择编号范围为 [n+1, n+gms) 的用户作为群成员,超出最大编号时从 0 重新开始选择。

消息发送

消息发送分为群聊消息私聊消息

  • 群聊消息:每个登录用户会向大群聊及其创建的群聊发送 gm 条消息(待实现频率控制:最多每秒 3000 条消息)。
  • 私聊消息:每个登录用户会向其所有好友发送 sm 条私聊消息(待实现频率控制:最多每秒 1000 条消息)。

卸载重装(待完成)

卸载操作通过删除本地数据文件并暂停一定时间来模拟,确保客户端停止发送心跳包,服务器检测到客户端下线并断开连接。

重装通过 SDK 初始化来模拟,卸载重装后程序会重新登录并执行后续检测操作。

检测操作说明

检测操作在 internal/checker 目录下实现,并采取循环检测的方式。在触发检测后,程序会进行一轮数据验证,只有当所有数据与预期结果一致时,才会继续执行下一步操作;否则将重复检测,直至数据符合预期。

检测操作的主要目的有两个:

  1. 阻塞主进程:确保异步的模拟操作已全部完成。
  2. 验证结果正确性:通过一系列正确性检测操作,验证模拟操作是否达到预期效果。

当前包含以下检测操作:

用户登录检测

用户登录检测发生在初始化或注册 之后,以及每次正确性检测操作 之前。通过计算与服务器成功建立连接的实际用户数量,比较预期登录用户数量。

预期用户登录数:

  1. 用户总数 * 登录率,向下取整。
  2. 所有用户总数。

好友数量检测

好友数量检测在导入好友操作完成后执行。由于离线用户无法同步数据,此检测仅针对在线用户的好友数量进行验证。

实际好友数量通过调用相应 SDK 获取,预期好友数量如下:

  • 超级用户:好友数量应为所有用户数。
  • 普通用户:好友数量应为所有超级用户数。

群聊数量检测

群聊数量检测属于正确性检测操作,通过调用 SDK 获取实际群聊数量。

预期群聊数量为:

  • 大群数量 + 常规群数量(常规群数量依据用户登录情况可能有所不同)。

会话数量检测

会话数量检测也属于正确性检测,通过 SDK 获取实际会话数量。

预期会话数量为:

  • 所有群聊会话数量 + 所有好友会话数量。

未读消息数检测

未读消息检测同样属于正确性检测,通过调用 SDK 获取实际未读消息数量,并与预期值进行对比。

预期未读消息数计算方式为:

  • 所有群通知消息 + 所有群消息 - (自己创建的群通知消息 + 自己发送的群消息) + 好友申请通过通知消息 + 好友消息。

需要注意的是,只有申请发起人会收到好友申请通过的未读通知消息,且根据导入好友 操作逻辑,只有超级用户的通知消息数量会有所不同。

注意事项

限制修改

  • 在模拟操作过程中,多个 SDK 实例同时运行,可能会对服务器造成较大压力,进而引发超时或其他问题。为确保系统在进行大规模数据量测试时能够平稳运行,需对以下几个关键指标进行调整:
  • 修改请求超时时间
  • 位置:openim-sdk-core/pkg/network/http_client.go
  • 调整内容:搜索Timeout,将请求的默认超时时间适当延长,以减少大数据量情况下的请求超时问题。建议根据测试规模和实际网络情况进行合理配置。
  • 原因:模拟大量 sdk 发起请求时,会对服务器造成较大压力,超时大多是因为 sdk 压力过大而非服务端,因此不会影响测试服务端性能的结果。
  1. sdk 异步管道超时时间
  2. 位置:openim-sdk-core/pkg/common/trigger_channel.go
  3. 调整内容:找到const timeout,将管道超时时间适当调大。
  4. 原因:模拟大量 sdk 接受服务端数据时,会对服务器造成较大压力,超时大多是因为 sdk 压力过大而非服务端,因此不会影响测试服务端性能的结果。
  1. 服务端通知超时时间
  2. 位置:open-im-server/pkg/notification/msg.go
  3. 调整内容:搜索WithTimeout,将context超时的时间适当调大。
  4. 原因:客户端和服务端在同一服务器会影响服务端性能,适当调大超时时间防止原本能正常传输的信息报错。
  1. 服务端日志级别
  2. 位置:open-im-server/config/log.yml
  3. 调整内容:将服务端日志级别remainLogLevel设置为 3。
  4. 原因:服务端日志级别默认为debug级别,主要为开发测试使用,会对性能产生影响。
  1. 设置通知消息未读状态
  2. 位置:open-im-server/config/notification.yml
  3. 调整内容:将 groupCreatedfriendApplicationApprovedunreadCount 参数设置为 true。此配置将确保在线用户接收到群创建和好友申请通过的通知消息时,依然将其标记为未读消息,方便后续的未读消息数量计算。
  4. 原因:统一 sdk 未读数的计算逻辑。
  1. 服务端最大连接数量
  • 位置:open-im-server/start-config.yml
  • 调整内容:根据测试时需要登录的用户数量,将maxFileDescriptors的值适当变大。
  • 原因:保证服务端能接受足够多的长连接。
  1. 动态端口范围
  • 运行命令(选择下面其一即可):
  • 当前会话:sudo sysctl -w net.ipv4.ip_local_port_range="10000 65000"
  • 永久:
         # 1) 直接写入(追加)到 /etc/sysctl.conf
         sudo bash -c 'echo "net.ipv4.ip_local_port_range = 10000 65000" >> /etc/sysctl.conf'
         
         # 2) 立刻生效
         sudo sysctl -p
         
         # 3) 验证
         cat /proc/sys/net/ipv4/ip_local_port_range
  • 原因:保证有足够的动态端口数模拟 sdk 长连接。