服务器资源: 8核16G内存, 6个机械磁盘,每个磁盘100G, 用于mongo分片,10MB带宽。
性能评估:同时在线用户10万,每秒钟发送消息900条,消息延时1秒(从发送者发出消息到接收到消息)
启动sdk,模拟50个用户在线、离线情况,消息可靠性100%。
发送10万消息,有3条失败,其他消息都能被对方精确收到,并成功落地本地db。对于失败的3条消息,接收方确实没有收到,系统消息是一致的。
OpenIM是由前微信技术专家打造的开源的即时通讯组件。Open-IM包括IM服务端和客户端SDK,是一套整体的解决方案,代码开源,一切可控,
OpenIM可以实现全平台支持,目前支持Android,iOS,Flutter,Uni-app,react-native, JSSDK等。
OpenIM可以应用在企业内部办公,dating交友,在线客服等项目,也可以用于元宇宙。
github地址:https://github.com/OpenIMSDK/Open-IM-Server
开发者中心: https://doc.rentsoft.cn/#/
在单机的情况下,模拟线上用户发消息流程,在线用户量和消息量达到一定量级后,系统CPU、内存、磁盘占用、以及消息时延情况。以确定用户群体达到一定量级后,对服务器资源的预先评估。本次测试并不极限测试,一是因为生产环境本来都会有用户量和消息量的限制,二是因为OpenIM的消息模型,消息发送首先都会通过websocket入库kafka,理论上发送消息的写入性能是两者的组合,而消息发送的真正瓶颈实际在mongodb的随机读写。
服务器资源: 腾讯云主机(香港)1台:linux Ubuntu 18.04.4系统,4核8G内存,单块机械硬盘。5Mb带宽。
测试条件:去掉消息入库mysql(因mysql仅用于管理后台,不影响线上用户服务)。日志级别调整为4或更低。kafka设置2个分区,msg_transfer 2个。
测试流程:1个客户端(成都,window pc,4核16G内存)启动1万个协程,模拟用户与服务器建立websocket长连接,间隔时间为随机50-100秒之间。两个客户端共模拟2万用户同时在线,发送消息,观察消息流转各个模块的处理能力,共计2500万条消息,观察系统内存、磁盘资源使用情况。
mongodb数据情况
redis数据情况
磁盘状态
资源占用分析
(1)redis内存消耗极小,一个用户一条数据(包括token和seq),和用户量成正比,3万用户占用几十M内存。
(2)mongodb如果去掉cache,内存消耗极小,每个document存放5000条消息,与用户量和消息量成正比,3万用户,2500万消息,索引才950K(更好的方式查看mongo消耗cache之外的内存)
(3)2500万消息,磁盘空间占用10G。
(4)每秒钟150条消息,cpu整体占用50%,即2核。
性能分析
(1)性能瓶颈在mongodb写入操作,1条消息,需要按照发送者和接收者拆分2次,mongodb写入2次,未来可以针对mongodb读写进一步优化。
(2)对于cpu消耗较大的模块,未来做一次整体优化。
(3)性能很平稳,不会随着数据量增加而降低。机械磁盘iops 达到200基本达到了设备的极限
服务器资源: 8核16G内存, 6个磁盘,每个磁盘100G, 用于mongo分片,10MB带宽。
性能评估:同时在线用户10万,每秒钟发送消息900条,消息延时1秒(从发送者发出消息到接收到消息)
(1)mongo集群部署,支持上亿用户同时在线,千亿级消息;
(2)简化集群部署;
(3)数据备份、恢复工具;
以上主要对服务端性能做了一个大致测试,但一套完整的IM解决方案,不仅仅是服务端的工作。实际上,客户端重要性毋庸置疑,具体包括如何利用seq和服务端同步消息,如果保证消息收发的时序,如何回调客户端(会话改变、新增,新消息),消息落地本地db,seq同步,消息推拉如何结合以确保消息收发可靠性。
相比于性能测试,实际上,消息的可达性(可靠性)更为重要。所以,我们在做性能测试的同时,也要对消息的可达性(可靠性)进行测试,如果不能保证消息收发的正确性,再高的性能也是徒劳。本文重点总结关于OpenIM对于消息可达性测试的方案、过程以及结果。先说结论,OpenIM消息可达率100%,大家可以放心使用在生产环境中。seq对齐和同步机制,保证了OpenIM的消息可达性是业界领先的。
IM消息系统的可靠性,通常就是指消息投递的可靠性,即我们经常听到的“消息必达”,通常用消息的不丢失和不重复两个技术指标来表示。确保消息被发送后,能被接收者收到。由于网络环境的复杂性,以及用户在线的不确定性,消息的可靠性(不丢失、不重复)无疑是IM系统的核心指标,也是IM系统实现中的难点之一。总体来说,IM系统的消息“可靠性”,通常就是指聊天消息投递的可靠性(准确的说,这个“消息”是广义的,因为还存用户看不见的各种指令和通知,包括但不限于进群退群通知、好友添加通知等,为了方便描述,统称“消息”)。
从消息发送者和接收者用户行为来讲,消息“可靠性”应该分为以下几种情况:
(1)发送失败,对于这种情况IM系统必须要感知到,明确反馈发送方。如果此消息没有发送成功,发送方可以选择重试或者稍后再试。
(2)发送成功,如果接收方处在“在线”状态,应该立即收到此消息。如果接收方处在“离线”状态不能收到消息,一旦上线则立刻收到消息。
(3)消息不能重复,用数学术语表示:“有且仅有这条消息”,如果重复了,可能表达的意思就变了。 总之,一个商用 IM系统,必须包含消息“可靠性”逻辑,才能谈基本可用,这是IM系统最基本也是最核心的逻辑。
互联网真实场景复杂,但客户端大体可以分为两种情况:(1)发送消息时,接收方在线,能收到消息;(2)发送消息时接收方不在线,登录后能收到离线消息。我们用测试程序模拟互联网客户端各种场景,按照登录、发送消息、接收消息的情况,把测试客户端分为以下2种类型:
(1)启动测试时离线,随机sleep 0-60 秒后登录,发送消息,且接收消息
(2)启动测试时离线,随机sleep 0-60 秒后登录,不发送消息,只接收消息
在实际测试中共计50个客户端,约25个(50%概率)客户端不发送只接收消息,约25个(50%概率)客户端发送且接收消息 。
发送模式:每个客户端随机选择其他客户端作为消息接收者;
测试预期: 每一条发送成功的MsgID,都能在接收的消息列表中找到,同样,每一条接收到的MsgID,都能在发送成功的消息列表中找到。
具体做法:(1)消息发送成功后,通过OnSuccess回调,记录MsgID; 收到新消息后回调OnRecvNewMessage,记录MsgID;(2)周期性对比两个消息列表,确认是否完全一致;
发送数据100000条,其中失败3条,9999997条成功,接收方成功接收9999997条消息(接收方成功接收到消息,写入本地db,并能触发消息回调)
每一条发送成功的消息,对方都能准确接收到,无论接收方在消息发送时的登录状态是在线还是离线。
每一条发送失败的消息,对方都不会收到。
注意事项:
(1)控制压力,因为sdk需要写本地db,客户端会成为压力瓶颈。
(2)压测客户端日志会影响测试性能。
此表格是某IM云平台的价格,如果按照10万月活,存储三年消息来算,大概每年需要支付15万。而采用OpenIM只需要采购云主机,每年成本约0.8万。
OpenIM文档方面的建设一直远远落后于开发, 也经常被开发者诟病,在接下来一周的时间里,我们重点补齐文档,让开发者更轻松接入。由于app sdk底层都是go来实现,所以本文先写一个模板和框架,在接下来的时间里,会有iOS、Android、Flutter、Uniapp、jssdk同事补充具体的细节,并更新到官网
初始化并登录成功回调,是正常使用OpenIM 服务的前提
OpenIM和调用方利用监听回调机制,和调用方信息互通,把登录状态通过异步回调方式即时传递给调用方,确保信息及时传达而不阻塞其主线程。
OpenIM调用流程分为如下几步:
(1)初始化:在整个生命周期执行一次
(2)设置监听:包括群组监听,好友监听,用户监听,消息及会话监听,这些监听会在其他章节中描述
(3)登录:登录回调成功后再执行其他操作,否则可能出现资源加载未完成的错误
(4)收发消息等操作;
OpenIM提供好友关系托管,比如好友申请,同意,以及好友获取等,OpenIM在客户端本地存储好友关系,并结合消息通知机制,按需调用后台接口,确保本地和服务端数据一致。同时利用监听机制,把数据变化通过异步回调传达调用方。OpenIM在收发消息时,默认不检查好友关系,您可以在配置文件中自行修改。OpenIM好友关系是双向关系,A添加B为好友后,则A在B的好友列表中,B也在A的好友列表中,A删除B,仅仅只是删除A的好友列表,而B的好友列表不受影响。同样,黑名单也类似。
好友监听器的作用:调用方设置好友监听,好友关系的变化会通过OpenIM消息机制实时同步到需要感知的在线用户,包括用户的其他端,调用方根据回调事件做相关的数据处理。
比如用户B在Android端添加用户A为好友,用户A(所有在线的终端)收到B的好友申请,则A调用GetRecvFriendApplicationList获取收到的好友申请列表,刷新界面。同时,用户B在线其他的终端比如PC端也会收到通知,B调用GetSendFriendApplicationList获取发起的好友申请列表,刷新B发出的好友申请列表。
当然也可以利用回调的参数信息,做增量处理,而不用获取全量的好友申请列表。
以用户B申请添加用户A为例
OpenIM继续领跑开源IM领域,在广大开发者的支持下,目前github star突破9k。在数据泄露、信息外泄、隐私滥用的时代,IM私有化部署需求旺盛。其中,政企协同办公对IM需求猛增,随着信息化技术的迭代升级以及信创产业加速落地和实践,协同办公软件的发展潜力将进一步被释放。“安全可控“逐步成为第一要素。对于社区交友领域,暴露出的隐私安全问题越来越多,私有化部署确保用户数据不泄露。
OpenIM从服务端到客户端SDK开源即时通讯(IM)整体解决方案,可以轻松替代第三方IM云服务,打造具备聊天、社交、办公功能的app。
github地址: https://github.com/OpenIMSDK/Open-IM-Server
开发者中心:https://doc.rentsoft.cn/#/
创始团队来自资深IM技术团队,我们致力于用开源技术创造服务价值,打造轻量级、高可用的IM架构,开发者只需简单调用 SDK,即可在应用内构建多种即时通讯及实时音视频互动场景。OpenIM优势:开源,安全,可靠,低成本。对于信息安全重视的电子政务,企业协同办公,OpenIM都是非常好的选择。
从公司成立之初就将“开源”作为核心战略来推进,开源充分体现了自由、平等、分享的互联网精神。
OpenIM邀请全球技术极客参与技术优化,让开发者轻松集成,让每一个应用都具备IM功能,同时考虑企业的接入成本、服务器资源以及最重要的数据安全性和私密性。
以办公场景为例,比如员工入职通知,放假通知等业务通知,由oa系统处理具体的业务逻辑,再调用消息推送api,触达到目标用户。
效果示例
以协同办公为例,员工收到系统推送的工作通知,有新任务需要处理。
员工点击工作通知,可以查看具体详情,每条通知有不同的发送者昵称和头像。这种通知类型是一种新的会话类型,全部聚合在同一个会话。
OpenIM简介
OpenIM:从服务端到客户端SDK开源即时通讯整体解决方案。开发者可以轻松替代第三方IM云服务,打造具备聊天、社交功能的app,也可以在自身应用中集成SDK,以提供即时通讯能力。开源IM的价值在于“一切皆可控”“我的信息我做主”,无论是技术,还是信息安全。
整体超过7.2K star,OpenIM作为开源IM的领跑者,持续添加新功能,包括消息推送api,群聊已读。服务端和sdk都是Apache-2.0 License授权协议,可以免费使用。带有UI的产品需要商业授权,有兴趣的客户可以联系我们深度交流。
5X8小时vip专属服务,包括:
(1)一对一技术咨询,微信 电话等各种方式;
(2)sdk使用辅导咨询;
(3)服务端架构及部署答疑;
(4)IM新需求优先排期;
(5)bug优先修复;
(6)系统维护和故障响应;
web端体验:https://open-im-online.rentsoft.cn/
安卓端体验:https://www.pgyer.com/OpenIM
iOS端体验:
https://testflight.apple.com/join/o956rTGx
单机性能及容量总结
服务器资源:8核16G内存, 6个机械磁盘,每个磁盘100G, mongo 分片,10MB带宽。
容量:用户同时在线10万以上,消息条数10亿。
性能评估:同时在线用户10万,每秒钟发送消息900条,(从发送者发出消息到接收到消息)消息延时1秒
可靠性总结
每条消息都能被对方精确收到,并成功落地app本地db。对于失败的3条消息,接收方确实没有收到,确保系统消息一致性。
系统具备优秀的平行扩展能力,除存储模块外,其他模块都无状态,支持亿级用户,千亿消息量。
github地址:https://github.com/OpenIMSDK/Open-IM-Server
开发者中心:https://doc.rentsoft.cn/#/
简要描述
管理员通过后台接口发送通知类型消息
请求URL
http://x.x.x.x:10000/manager/send_msg
请求方式
POST
请求示例
{
"operationID": "Oa notification operationID111",
"sendID": "openIM123456",
"recvID": "18712345678",
"senderPlatformID": 2,
"senderFaceURL": "http://www.head.com",
"senderNickname": "系统通知2",
"content": {
"notificationName": "公文管理",
"notificationFaceURL": "http://www.head1.com",
"notificationType": 1,
"text": "大家好,今天是...",
"externalUrl":"",
"mixType": 0,
"pictureElem": {
"sourcePath": "",
"sourcePicture": {
"uuid": "",
"type": "",
"size": 0,
"width": 0,
"height": 0,
"url": ""
},
"bigPicture": {
"uuid": "",
"type": "",
"size": 0,
"width": 0,
"height": 0,
"url": ""
},
"snapshotPicture": {
"uuid": "",
"type": "",
"size": 0,
"width": 0,
"height": 0,
"url": ""
}
},
"soundElem": {
"uuid": "",
"soundPath": "",
"sourceUrl": "",
"dataSize": 0,
"duration": 0
},
"videoElem": {
"videoPath": "",
"videoUUID": "",
"videoUrl": "",
"videoType": "",
"videoSize": 0,
"duration": 0,
"snapshotPath": "",
"snapshotUUID": "",
"snapshotSize": 0,
"snapshotUrl": "",
"snapshotWidth": 0,
"snapshotHeight": 0
},
"fileElem": {
"filePath": "",
"uuid": "",
"sourceUrl": "",
"fileName": "",
"fileSize": 0
},
"ex": ""
},
"contentType": 1400,
"sessionType": 4,
"isOnlineOnly": false,
"offlinePushInfo": {
"title": "admin revoke your message",
"desc": "",
"ex": "",
"iOSPushSound": "default",
"iOSBadgeCount": false
}
}Copy to clipboardErrorCopied
请求参数
参数名类型必选说明
operationIDstring是操作ID,保持唯一,建议用当前时间微秒+随机数,用于后台链路追踪问题使用
sendIDstring是管理员ID,为后台config文件中配置的管理员ID中一个,默认openIM123456
recvIDstring是接收者userID
senderPlatformIDint否发送者平台号,模拟用户发送时填写, 1->IOS,2->Android,3->Windows,4->OSX,5->Web,5->MiniWeb,7->Linux
senderFaceURLstring否发送者头像,用于客户端通知会话产生
senderNicknamestring否发送者昵称,用于客户端通知会话产生
contentobject是消息的具体内容,内部是json 对象
notificationNamestring是通知标题
notificationFaceURLstring是通知头像
notificationTypeint是通知类型,如:1代表入职通知,2代表离职通知
textstring是通知正文e
externalUrlstring否通知点击后需要跳转到的地址链接(不填则无需跳转)
mixTypeint是通知混合类型 0:纯文字通知 1:文字+图片通知 2:文字+视频通知 3:文字+文件通知
pictureElemobject否图片元素对象
sourcePictureobject否原图
bigPictureobject否大图
snapshotPictureobject否缩略图
soundElemobject否声音元素对象
videoElemobject否视频元素对象
fileElemobject否文件元素对象
uuidstring否对象唯一ID用于缓存使用
type/videoType/string否图片类型/视频类型
size/dataSize/videoSize/snapshotSize/fileSizeint否多媒体文件大小,单位字节
width/snapshotWidthint否图片/视频缩略图宽度
height/snapshotHeightint否图片/视频缩略图高度
url/sourceUrl/videoUrlstring否图片/文件/视频的URL
sourcePath/soundPath/videoPath/filePathstring否文件路径,可不填写
fileNamestring否文件名字
exstring否扩展字段
contentTypeint是消息类型固定为1400
sessionTypeint是通知会话类型固定为4
isOnlineOnlybool否改字段设置为true时候,发送的消息服务器不会存储,接收者在线才会收到,不在线该消息丢失。
offlinePushInfoobject否离线推送的具体内容,如果不填写,使用服务器默认推送标题
titlestring否推送的标题
descstring否推送的具体描述
exstring否扩展字段
iOSPushSoundstring否IOS的推送声音
iOSBadgeCountbool否IOS推送消息是否计入桌面图标未读数
返回示例
{
"errCode": 0,
"errMsg": "",
"data": {
"serverMsgID": "",
"clientMsgID": "",
"sendTime": 1645697804432
}
}Copy to clipboardErrorCopied
返回参数
参数名类型说明
errCodeint0成功,非0失败
errMsgstring错误信息
sendTimeint消息发送的具体时间,具体为毫秒的时间戳
serverMsgIDstring服务器生成的消息的唯一ID
clientMsgIDstring客户端生成的消息唯一ID,默认情况使用这个为主键
消息类型格式描述
简要描述
管理员消息发送字段中contentType支持的消息类型说明以及消息content的具体字段说明。
ContentType消息类型说明
ContentType值类型说明
101文本消息
102图片消息
103音频消息
104视频消息
105文件消息
106群聊中的@类型消息
107合并转发类型消息
108名片消息
109地理位置类型消息
110自定义消息
111撤回类型消息
112已读回执类型消息
114引用类型消息
Content具体内容
content内部为具体的json对象,不同的消息类型是不同的json对象
文本消息
{
...,
"content": {
"text": "nihao"
},
....
}Copy to clipboardErrorCopied
参数名必选类型说明
text是string文本消息的具体内容
自定义消息
{
...,
"content": {
"data": "",
"description": "",
"extension": ""
},
....
}Copy to clipboardErrorCopied
参数名类型必选说明
datajson string是用户自定义的消息为json对象转换后的string
descriptionjson string否扩展的描述信息为json对象转换后的string,可以不使用
extensionjson string否扩展字段,暂时不使用
欢迎分享,转载请注明来源:夏雨云
评论列表(0条)