Rust语言在IM客户端的实践

本文为来自 抖音电商-前端-平台解决方案团队 成员的文章,已授权 ELab 发布

大厂技术  精选好文  IM x Rust

概述:

不管咨询来的有多凶,客服软件不能崩 — 某抖音电商研发

本文将介绍飞鸽 前端团队如何结合Rust对飞鸽客户端接待能力进行提升,如何一步步从概念验证、路径分解走到分工开发,到最后完成上线收益论证,并分享其中挑战与经验。

本项目是一个长周期的复杂项目,相信本项目落地的经验对其他同学及团队能有所借鉴。

背景:

飞鸽是在抖音电商业务上面向商家和用户的聊天工具,其拉通售前、售中、售后渠道,为商家履约提供重要支撑。

对于飞鸽桌面端IM而言,我们会面临很多基础挑战,比如做好会话稳定性、操作流畅性、冷启动速度等,而在满足98%以上的用户需求且业务趋于稳定后,一些在冲刺后遗留的性能天花板问题暴露在我们面前,其中 高并发接待 & 多开是两个重要的挑战,是旧账与难啃的硬骨头。


具体场景负反馈
高并发进线场景猛进线,同时接待 > 2001.  抖音直播起量爆单
2.  购物节消费券、满减券等营销活动场景
3.科技新品发售
在高并发进线下,页面严重卡顿
多开场景1.客服服务于多个店铺
2.商家经营多个店铺
多开数无法满足部分客户需求

为何持续会有这些挑战存在?

  1. 历史技术选型,包含者成本、人力、效率等考量,飞鸽客户端使用的技术栈是react + electron

    1.  im sdk与业务渲染代码都由 js 编写,im sdk同时是cpu密集型 & io 密集型的组件,在高并发场景下,渲染频率也比较高,业务与sdk相互抢占cpu资源与io资源,导致收发消息慢、操作卡顿(高并发限制)。

4c170b40da1e469482b6dace0a295856.jpeg

b. 由于im sdk运行在webview中,所以收发消息依赖webview存活,故多开账号 = 多个webview,内存成本线性增长

937695e41f2fa1716ef11b857fc43faa.jpeg 2. im页面在web层面多次优化后已接近架构上限,无法基于现有架构做更多天花板的突破

对于以上挑战,我们给出的解法是:对现有架构进行调整,使用Rust语言对im sdk进行重写,彻底解除这一块的性能瓶颈!

76b5d4ec0daf3f0b2ac64f856a21e096.png

为什么是Rust ?

飞鸽im sdk是一个对运行稳定性要求高的组件,其工程量大、逻辑复杂,对于异步特性使用非常频繁,其对于内存安全、 线程安全有着比较严格的要求。

假如使用C++,作为新手并没有把握能够将复杂的IM SDK少bug的编写下来(团队限制)

Rust学习曲线虽然陡峭,但是其为安全设计的各类语言特性、强大的编译器,能够将新人编写代码的问题数降到最低(逻辑问题除外)。

并且飞书团队提供了客户端的rust生态库,帮助我们解决很多的基建问题,所以这里使用Rust是相当合适的。


难度内存安全异步写法便捷性线程安全开发心智负担基建/库
RustHardRust编译器保障语法机制层面支持async await机制支持(所有权、互斥锁等),编译检查半成熟
C++Hard人肉保障C++11引入async future - 难用 C++20引入协程 - 依然难用人肉保障成熟

Rust学习成长曲线:

630d774be251ed9fc21c58eee7534c83.png

飞鸽IM客户端历史架构

05716822f074f1724ba4e76d9710db43.png

如背景中所描述,历史架构存在这两个问题:

  1. IM SDK 与 业务JS代码共用Weview资源,接待密集的时候,sdk与业务,互相抢占cpu与io资源,导致容易卡顿、消息延迟。

  2. 多开的账号必须依赖IM Webview存活(否则无法收到消息),内存线性增长。

新架构与预期优势

8f78dc2fc23f0e05c5257536b885b404.png

  1. Rust独立进程承担所有的im sdk的计算压力,可以大幅减轻js线程压力,可提升压力场景接待体验。

  2. Rust im SDK 解除浏览器中的IO限制(如同域名并发数限制)。

  3. 解除Webview存活依赖,依靠rust进程也可收消息,为更多账号的多开能力提供了铺垫。

27383eed8209dbb9156c4e67ba38137b.jpeg

概念验证(前置)

为了验证推测切实可行,我们提前做了完备的POC验证。

在POC中,我们针对“单进程单线程模型”、“多进程模型”、“多线程模型”,这三种模型搭建了mvp demo,即简易的客服聊天模型,并进行压力测试,并监测其内存、cpu等指标,得出的结论是:

e45c6ce79743fa5f8e6b45c483c0a504.png

  • rust 整体优于 js,计算占比越重,优势越明显(高压时cpu差别能到达3倍以上)

  • 架构选型上,rust进程独立是最好的方案,稳定性更优、性能损耗相差较小

路径分解:

路要一步步走,整个项目粗估下来会有上百的工作日,作为业务团队,我们无法在短期内投入大量的资源去做这个项目,所以需要一步一步拆解、验证、拿收益。

团队内native开发资源有限,这件事情的进行也需要团队进行学习、成长

Rust SDK工程基建

造房子先得有一个地基 —— Rust工程的基础建设,是Native业务的前置条件!

桌面端同学牵头搭建了整个RustSDK地基,地基解决的问题如下图所示:

471725428d282d6d8d9d355a4469aa75.png

需要做的工作:

  1. 业务容器 — 有规律的组织代码结构,进行业务隔离、资源隔离

  2. 跨进程调用封装 — 降低业务调用难度

  3. 建设日志系统、日志回捞 — 降低排查问题的难度

  4. 构建跨平台异步执行环境 — 简化异步代码编写,底层封装,便于跨平台代码迁移

  5. 跨平台编译,跨平台集成

  6. ...

2853a9a19fd212940eec45ce86cd4bda.png

阶段一:IM基础能力夯实

在拥有一部分地基后,我们开始针对IM SDK的基础能力进行实现和验证****

完成基础能力验证之后,我们才会有信心在新的架构上叠加更多的功能。

775546116e362ff1f1a40d314cb54886.jpeg

先走一小步

这阶段我们关注以下指标, 希望其存在优化,至少不劣化

  1. 长链在线率

  2. 消息发送成功率

  3. 卡顿率

  4. Rust进程崩溃率、无响应率

实现功能&阶段性论证:

  1. 仅实现长链能力下沉,验证&提升其稳定性

4d0cea710fe11d1223c808fbbe14b6ba.png

  1. 本阶段论证结果如下:

    1. Rust Crash率, 达成预期

    2. Rust无响应率 - 未达预期,可优化

    3. 长链在线率 - 达成预期,但是存在优化空间

    4. 卡顿率 - 不劣化 达成预期

    5. 消息发送成功率 - 不劣化,达成预期

这阶段的工作是考验耐心的,因为这个阶段并不能带来实质性的用户体验提升、也无法拿到明显的提升数据,只是作为中间阶段,它有存在的必要性。

c0c5c80fa688e85b0f41097097f7637f.jpeg

这阶段后,在稳定性治理、基础能力验证、 Rust 语言经验、指标制定合理性这几方面,我们踩上了一个更结实的台阶,更有信心去进行更复杂的下一阶段。

阶段二:使用Rust实现IM SDK全部能力

夯实基础后,我们开始发力冲刺,大刀阔斧的对IM SDK进行重新设计、实现、联调以及上线

此阶段要实现im sdk的全部能力、 并对线上运行的js im sdk进行替换

由于飞鸽im对于通信模块的稳定程度要求是很高的,替换过程就像是在高速行驶的车辆上替换轮胎,如果出现问题也容易导致大量的客服负面反馈。

因此,新rust sdk的稳定性、异常问题时的兜底方案、灰度时的监控观察、对新增反馈的留意都很重要,放量过程会存在一定精神压力。

c2d4137c49cfb11117cc9912fd5f2d62.jpeg

工作内容大致如下:

  1. 多实例的Rust IM SDK设计(商家单聊、群聊、平台客服)、Js -> Rust IMSDK跨端调用协议设计

    1. 分析、拆解所有Js Im SDK至今具有的能力,并以贴合Rust的方式重新设计

    2. 需要在协议设计中,尽可能的合并 & 简化 Js -> Rust的调用,以减少IPC通信成本

  2. 开发

    1. Rust IM SDK核心实现

    2. Rust\Js适配同学紧密合作,根据协议进行业务实现、业务适配

    3. 密切沟通,发现问题及时纠偏

      4ab24e50edeed620b9cf77879aa4232d.jpeg


    4. 编写单测

  3. 测试

    1. 各类IM场景回归测试

    2. 性能进行验证

  4. 异常兜底方案实现

    1. 设计数据冗余,当Rust进程出现崩溃、无响应、不可恢复的网络错误时,识别并fallback到 web版本,使用冗余数据快速恢复im sdk正常运行状态,确保用户体验。

  5. 稳妥的上线方案 & 稳定性治理

  6. 调用&适配优化,结合Native能力进一步性能优化

  7. 结果回收

其中各个步骤都会存在一些挑战,在后后面的内容会提到。

调用简化模型:

7d245158a4278703e6e07e71e06e117e.jpeg

IM Core简化模型:

cf8dd6fc2edaf0145765c46c4e4c74e2.png

阶段三:基于稳定的RustIM SDK实现形态升级

最后的阶段,我们基于完善的Rust IM SDK的能力进行形态的升级

本阶段正在进行中,完成后会做更多的分享

  1. 多窗口改造

    1. 销毁后台的多开账号,让多开账号数量突破到25个

621288078228a17781a2565d9b620063.jpeg
  1. 消息提醒、通知流程改造

  2. 消息本地化能力 - 加快消息上屏

挑战与应对经验:

编程语言 & IM领域知识突破

一个有战斗力的团队,一定是持续学习、进步的

  1. 获取学习的纯粹快乐

    1. 当沉浸在学习中,并感受到自己在进步的时候,会是一个快乐的状态

  2. 逐步克服小挑战,及时获得正反馈

  3. 在同事中找到伙伴和老师,询问与探讨

    1. 建立团队中的学习氛围

0a9fde2a193353bb1d87a2b22da5dd26.jpeg

长周期技术项目,如何持续保持信心 ?

  1. Leader与同事认可与支持 — 团队基础、价值观鼓励

  2. 关注长期收益,训练自己延迟满足感

  3. 做好阶段性分解与验证,缩短单个周期

    1. 如本文的一二阶段拆解,可逐步累积信心

  4. 增强自身实力,做好问题把控,及时发现&解决问题

0c33995d95b6496ddfabfc60d09503f3.jpeg

高效合作

团队Native开发同学少,且各自并行业务需求,需合理的安排开发路线,减少总开发时长。

  1. 合理的设计开发并行路线,减少串行依赖

27adbe38dc22ba63ef889509a54476b1.jpeg

  1. 协议与接口先行

  2. 各同学负责其相近&擅长的部分

  3. 联调时缩短彼此距离,高效沟通

8e4ca2d9eafc12bd71b53ae75a997274.jpeg

保障用户体验的灰度上线

  1. 编写模块的健康自检,检测到异常时用最小的代价切换备用老方案

  2. 完善业务监控&技术指标监控

    1. crash率、无响应率、长链在线率、发消息成功率、请求成功率、卡顿率等

  3. 对真实用户使用体验进行跟踪

    1. 飞书反馈群组维护,及时获得用户反馈

    2. 与商家客服保持线下联系,获取一手体验情况

  4. 放量节奏的把控

    1. 大型改动可以先给白名单用户试用,收集反馈

    2. 放出能够识别问题的量,解决问题后再继续放量

    3. 放量期间主动查询用户实时反馈数据,有问题及时解决

      ea38525f409af5c06f8a92275944ee86.jpeg

如何减少IPC通信成本带来的开销

频率过高的IPC通信可能使得CPU优化适得其反,因为老版本都运行在Js中,所以调用频率是没有节制的(循环读取数据也经常出现),必须要在设计上降低下来 —— 降低业务JS线程的压力

以下措施可以将本场景通信成本降低90%以上

  1. 更高效的数据协议 protobuf

    1. vs json: 数据更小、解析和序列化性能更高、跨语言生成代码工具

  2. Rust push to js :

    1. 使用数据收集去重 + debounce批量更新的策略,合并多个数据回调接口,减少通信频率

  3. Js call rust:(单次基础耗时4ms)

    1. 适当缓存数据,不用每次都回源查询

    2. 需要频繁调用的逻辑下沉Rust,Rust逻辑自完善

结果回收:极端场景下的优化大盘数据体现不明显

60b6309c2235e6ed34098cfdd7b874df.jpeg

针对某种场景做的优化工作不容易在大盘数据中得到体现(尤其在灰度阶段),我们应该针对特殊场景建立新指标:

  1. 编写策略,识别并收集极端场景下的数据

    1. 为了衡量极端场景的的卡顿优化,建立了忙碌与卡顿指标,可以衡量出用户接待忙碌程度与卡顿率的关系,并且通过此指标将优化清晰的衡量出来

Rust SDK的问题治理

  1. 前期的问题不稳定,需更多信息辅助排查,日志尽量完整

  2. 真实用户群体保持联系,可加快问题验证、问题发现的过程

  3. 需要建设便捷的日志回捞 & 日志分析工具

    1. 帮助快速找到日志还原现场

      cfe66e2a510d7e037eb8f29820e865ae.jpeg

收益

压力评测:


优化前优化后
高压测试场景卡顿、进线慢、发消息慢持续累积至不可使用持续流畅、稳定可用

数据表现:

8e7644e3be895094ce33ddf52e65eae1.jpeg
  1. 客服发送消息,大盘端到端耗时降低 40%

  2. 消息发送成功率三个9 -> 四个9

  3. im页面大盘卡顿率降低 15%

  4. 密集接待场景,卡顿率降低 50%

全量至今,再无大量进线导致卡顿的反馈

回访历史反馈用户,皆无因大量接待导致的卡顿现象

❤️ 谢谢支持

以上便是本次分享的全部内容,希望对你有所帮助^_^

喜欢的话别忘了 分享、点赞、收藏 三连哦~。

c85a067c733d53bb887855ea708446dc.png

往期推荐

3.40秒到231.84毫秒,我用Performance面板分析性能瓶颈全流程

52fdedee33a12fe9be90cd216c06dcde.png

开发过程中,因为国际化太麻烦,搞了这款 vscode 国际化插件

46abb84b8f613bf8d6bf36134db59df5.png

Vue Hooks: 让Vue开发更简单与高效

bc84265b5ba661685c6b40d131ceb65e.png


最后

  • 欢迎加我微信,拉你进技术群,长期交流学习...

  • 欢迎关注「前端Q」,认真学前端,做个专业的技术人...

098392dfdee74e838b1652e7b590c574.jpeg

27f17170dc791574164221eee5411649.png

点个在看支持我吧

9119d35442cbb79c45840d55884736bd.gif

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值