1.2 自研还是买云 IM 服务

场景

立项第一周,我们开了三天会,核心议题只有一个:自研还是买云 IM 服务。市面上有不少成熟的 IM 云服务(腾讯云 IM 之类的商品化方案、以及一批第三方 SaaS),按月活或日消息量计费,几天就能接入完毕。团队里分两派:业务派说买,两周上线不香吗;技术派说自研,数据在别人手里将来很被动。我们谁也说服不了谁,干脆把两边的账都摊开算了一遍。

方案

买服务的好处很直观:SDK 现成、多端齐全(iOS、Android、Web、小程序全覆盖)、各家手机的推送通道人家都打通了,音视频也能顺手接上。坏处同样直观:计费随用户量线性增长,DAU 一旦上去,月费比自研的机器费还贵;而且消息数据存在别人机房里,我们做的是协作工具,消息里有客户的合同和图纸,合规评审那一关就过不去。另外云服务的能力是通用的,我们要做的 @提及定向提醒、消息撤回回执、多端同步策略定制,往往要排期等厂商做,甚至有些根本不支持。

自研的好处是数据自主、成本可控(只算机器和人力)、功能想怎么改怎么改。坏处是投入大——至少两人三个月才能出一个能用的版本,弱网重连、连接风暴、消息乱序这些坑一个都躲不掉,而且稳定性得自己背。

我们还算过一笔具体的账。买服务按 DAU 计费,假设十万 DAU、每人日均五十条消息,一年服务费在六位数量级。自研的话,两台网关 CVM 加一台逻辑层、一个 TDSQL-C 小实例加一个 Redis,初期每月成本也就服务费的零头,人力成本另算。DAU 过了五万这道坎之后,自研开始明显省钱;低于一万 DAU,服务费更低还省心。这个临界点每个厂商不太一样,建议拿自家报价单实算,别信销售嘴上的数字。

账一旦算清楚,选型的争论就结束了。技术派赢在合规和长期成本,业务派也会在三个月后承认这个决定是对的,虽然他们当时并不这么想。自研逼着团队吃透长连接、存储、消息队列,这套经验对后续任何后端系统都是资产。买服务则省心但能力始终在厂商那里。估值时把这条也算上,自研的账面会更好看一些,前提是团队能稳定。

维度 云 IM 服务 自研 我们的选择
上线周期 一到两周 两三个月 自研(排期允许)
长期成本 按 DAU 计费线性增长 固定机器成本为主 自研
数据合规 数据存第三方 全在自有库 自研(硬项)
定制能力 受厂商能力边界约束 完全自主 自研
稳定性兜底 厂商 SLA 自己背锅 买服务占优
多端 SDK 现成 自己写 买服务占优
团队储备 无需 IM 经验 需要长连接经验 自研(有 Go 底子)
运维监控 自带控制台 自建 买服务占优

结论很清楚:合规和成本两条硬项决定了我们必须自研。但说句公道话,如果你只是做个客服聊天或者小规模社群,DAU 在几千量级,买服务明显更划算,别为了自研而自研。自研省下的钱本质上是把自己人力折算进去了,小团队未必划算。

还有一种折中方案值得提:核心自研、边缘外采。长连接、消息存储、必达逻辑这些自己写;推送通知这种碎片化严重的买厂商服务;客服会话这种边缘场景用 SaaS。我们最终就是这条路,既保住了核心数据,又没在适配各国厂商推送上耗尽人力。这些判断在立项阶段不可能一次全对,我们的经验是定下主路线后每周做一次小复盘,把新冒出来的不确定性写进决策日志,三个月积攒了四十多条。

配置与代码

自研路线确定后,我们划出了最小可用边界(MVP),先把单聊和群聊跑通,其他花哨功能一律砍掉,等跑稳了再补。功能边界一旦定死,排期就从模糊的三个月变成可交付的版本列表,这是自研项目里最容易被忽视的管理动作:

// MVP 阶段只实现这些指令,其余一律不做
var MVPCommands = map[string]Handler{
    "login":       handleLogin,     // 登录换票
    "send_msg":    handleSendMsg,   // 发消息(仅文本)
    "sync":        handleSync,      // 拉新消息
    "ack":         handleAck,       // 确认已收
    "read_report": handleRead,      // 多端已读同步
    "heartbeat":   handleHeartbeat, // 心跳保活
}

MVP 的部署也很克制:两台 CVM 跑网关与逻辑层,一个 TDSQL-C 实例做存储,一个 Redis 实例做未读数与会话列表,先不引入消息队列,等压测出写入瓶颈再加 RocketMQ(见 削峰填谷:RocketMQ 异步写入)。这样一套在云控制台上一上午就能拉起来,机器规格的选择参考 机器测算:CVM 撑多少连接。

自研的技术栈我们也做了取舍表,列在这里给同样纠结的人一个参照:

组件 商品化替代 我们为什么自己写 我们为什么买
长连接网关 IM SDK 自带 核心能力,必须自主 —
推送通知 厂商推送服务 — 各手机厂商适配太碎
消息存储 — — TDSQL-C 买现成的
未读数 — 逻辑简单自己写 Redis 买现成的
日志审计 — — CLS 买现成的

这张表帮我们在评审会上省了一个小时——凡是写着买现成的,就不再花人力;凡是核心能力,一个都不让。

顺带说一下灰度迁移方案的伏笔。即使全自研,也不意味着所有客户端版本同步切换。我们约定每个客户端版本在握手时上报自己的协议版本号,服务端按版本走不同处理分支,这样老版本客户端在新服务端上线后还能活两到三个版本周期。这套版本意识后来在 灰度发布:按用户分桶放量 里发挥了关键作用。

踩坑

最大的坑不是技术,是低估了多端客户端的活儿量。我们以为写了服务端,客户端 SDK 省事。实际上 iOS 后台被杀进程后的重连与唤醒、Android 各厂商推送通道的适配、网页端跨标签页的同步,每个都能吃掉团队一周以上的时间。如果有条件,推送通知这部分可以考虑直接买第三方推送服务,只把长连接的核心能力攥在手里,这是性价比最高的妥协。

第二个坑是估值模型错位。我们最初按峰值 QPS 估算机器配置,结果发现 IM 的特点是连接数高但 QPS 低,机器瓶颈在内存和连接数不在 CPU,按 QPS 估算买了一堆高 CPU 机器,内存反而不够。这块在 机器测算 里专门算了一遍账。

第三个坑是售后与 SLA。我们一度想用某家 SaaS 的售前承诺做兜底,真把合同条款要来看,发现 SLA 全是免责条款,出事最多赔当月月费。自研的锅虽然自己背,但至少修不修、什么时候修自己说了算。

第四个坑是迁移成本被低估。其实自研派在争论里隐藏了一个假设:买了云服务就永远不能自研。事实是很多团队先买后自建,但云服务的 SDK 深入客户端各端之后,迁移等于把客户端重写一遍。我们庆幸自己一开始就选了自研,否则这个沉没成本会非常大。

第五个坑是自研早期缺少监控。买服务自带控制台,自研初期什么都没有,出了问题两眼一抹黑。我们前两周全靠日志文件 grep 排障,直到把监控告警体系搭起来才算能睡安稳觉,这段经历写在 监控告警:连接数与积压 里。自研的隐性成本里,监控这一项一定得提前算进去,它属于那种平时不显眼、出事就是大事的投入,和日志审计一样属于底座型工作。

小结

自研与否不是技术问题,是商业问题:看数据敏感性、看用户量级、看团队有没有长连接经验。我们的结论是合规驱动自研、成本奖励自研,代价是三倍工期和多端兼容的无尽琐事。一旦决定自研,接下来就是算机器、定协议,进入真正的实战,这也是第二篇和第三篇的全部内容。书里后面所有章节都建立在自研这条路上,也与 腾讯云实践 里的其他自研案例可以互相印证,建议两本对着看。

回过头看,选型会上最怕的不是选错,而是没有可推翻的决策依据。我们的表格、账单和决策日志,价值不在于一次选中正确方案,而在于三个月后回头看时,知道当初为什么这么选,以及现在该改什么。

补充一个选型时少有人提的维度:退出成本。无论自研还是外采,都要问如果两年后要换掉它,代价是什么。自研的退出成本是把协议接口文档化(后来我们真的把内部协议开放给了生态伙伴),外采的退出成本是数据导出与协议反向兼容。我们与云 IM 服务商谈判时把数据导出条款写进了合同补充页——这条建议值得每个选型阶段的人抄进采购清单,谈判里往往让步最快的也是这类非标条款。另外要做一次小规模试点再签长约:我们让一个边缘业务模块先跑了三个月,跑通了才规模化,避免选型评审会上的纸面判断决定全系统的命运。

补充一条很多团队走过的中间路线:不必非此即彼。我们最终的形态是核心链路自研、外围能力买现成组件——消息存储、网关、序号分配这些决定系统脾气的部分捏在自己手里,但推送通道的厂商对接、富媒体文件的存储与加速、短信验证码这类非核心又琐碎的能力直接用云组件。这样团队规模始终控制在能开饭的范围,真出了问题需要深挖的环节也都在自己掌控之内。判断标准很简单:这块能力如果挂了,用户三分钟内会不会骂你,会就自研,不会就可以外包。

延伸阅读