Rust语言在IM客户端的实践

概述:

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

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

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

背景:

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

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

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

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

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

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

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

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

为什么是Rust ?

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

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

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

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

Rust学习成长曲线(网图):

飞鸽IM 客户端历史架构:

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

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

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

新架构与预期优势:

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

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

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

概念验证(前置)

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

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

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

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

路径分解:

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

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

Rust SDK工程基建

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

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

需要做的工作:

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

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

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

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

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

  6. ...

阶段一:IM基础能力夯实

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

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

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

  1. 长链在线率

  2. 消息发送成功率

  3. 卡顿率

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

实现功能&阶段性论证:

  1. 仅实现长链能力下沉,验证&提升其稳定性
  1. 本阶段论证结果如下:

    1. Rust Crash率, 达成预期

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

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

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

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

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

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

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

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

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

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

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

工作内容大致如下:

  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. 密切沟通,发现问题及时纠偏


    5. 编写单测

  3. 测试

    1. 各类IM场景回归测试

    2. 性能进行验证

  4. 异常兜底方案实现

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

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

  7. 结果回收

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

调用简化模型:

IM Core简化模型:

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

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

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

  1. 多窗口改造

    1. 销毁后台的多开账号,让多开账号数量突破到25个
  1. 消息提醒、通知流程改造

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

挑战与应对经验:

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

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

  1. 获取学习的纯粹快乐

    1. 当沉浸在学习中,并感受到自己在进步的时候,会是一个快乐的状态
  2. 逐步克服小挑战,及时获得正反馈

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

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

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

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

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

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

    1. 如本文的一二阶段拆解,可逐步累积信心
  4. 增强自身实力,做好问题把控,及时发现&解决问题

高效合作

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

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

  2. 协议与接口先行

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

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

保障用户体验的灰度上线

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

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

    1. crash率、无响应率、长链在线率、发消息成功率、请求成功率、卡顿率等
  3. 对真实用户使用体验进行跟踪

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

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

  4. 放量节奏的把控

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

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

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

如何减少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逻辑自完善

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

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

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

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

Rust SDK的问题治理

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

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

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

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

收益

压力评测:

优化前:卡顿、进线慢、发消息慢持续累积至不可使用

优化后:持续流畅、稳定可用

数据表现:

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

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

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

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

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

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

❤️ 谢谢支持

以上便是本次分享的全部内容,希望对你有所帮助^_^,喜欢的话别忘了 分享、点赞、收藏 三连哦~。

JD: job.toutiao.com/s/idtCDDtX [上海], job.toutiao.com/s/idbKjTVG [杭州]

有兴趣的同学可以添加我们团队的微信 tt_ec_fe 详细沟通。

相关推荐
C语言魔术师12 分钟前
【小游戏篇】三子棋游戏
前端·算法·游戏
匹马夕阳1 小时前
Vue 3中导航守卫(Navigation Guard)结合Axios实现token认证机制
前端·javascript·vue.js
你熬夜了吗?1 小时前
日历热力图,月度数据可视化图表(日活跃图、格子图)vue组件
前端·vue.js·信息可视化
桂月二二8 小时前
探索前端开发中的 Web Vitals —— 提升用户体验的关键技术
前端·ux
hunter2062069 小时前
ubuntu向一个pc主机通过web发送数据,pc端通过工具直接查看收到的数据
linux·前端·ubuntu
qzhqbb9 小时前
web服务器 网站部署的架构
服务器·前端·架构
刻刻帝的海角9 小时前
CSS 颜色
前端·css
九酒9 小时前
从UI稿到代码优化,看Trae AI 编辑器如何帮助开发者提效
前端·trae
浪浪山小白兔10 小时前
HTML5 新表单属性详解
前端·html·html5
lee57610 小时前
npm run dev 时直接打开Chrome浏览器
前端·chrome·npm