【编者按】《博文共赏》是 InfoQ 中文站新推出的一个专栏,精选来自国内外技术社区和个人博客上的技术文章,让更多的读者朋友受益,本栏目转载的内容都经过原作者授权。文章推荐可以发送邮件到 editors@cn.infoq.com 。
业务背景
最近,微博平台的群发业务增长迅速,一个月内业务量暴涨 70%,出现两次负载过高报警,通过紧急扩容解决。仔细思索,这一类业务场景在移动互联网中非常普遍,值得深入探讨。
在微博通讯系统中,有两种常见的模式,一种是单点对单点的私信通讯方式,另一种是单点对多点的群发通讯方式,后面一种主要使用在粉丝服务平台。最近几年,通讯内容趋向于富媒体,包含图片、网络文章、音视频文件等,微博统一将各类资源抽象为卡片(Card),平台负责 Card 数据的存储与处理,前端和客户端根据不同的模板渲染 Card。
微博服务号基本都是每天早上向所有粉丝推送一条消息,这就是群发场景,同一时段高并发的请求访问给后端系统带来巨大压力。
问题挑战
对于群发业务,增加缓存能挡住大多数的流量,但这里还有诸多挑战:
- 缓存大小规划,如果所有内容都缓存,成本直线上升,虽然可以抗住峰值时段流量,但是非峰值时端,资源严重浪费。
- 缓存资源有状态,很多 Card 并非完全是静态资源,根据请求参数不同返回数据不同。
- 同一资源集中访问间隔时间非常短,经常维持在几分钟内,还有一个特点,5% 的资源占据了 80% 以上的访问请求。
弹性计算 or CDN?
问题一:能否使用云服务中的弹性计算解决****?
如果业务爆发周期为一年中的某一季节或者某些节日,弹性计算可以较好地解决这个问题,如果业务爆发周期为一天中的某一时段,从运维管理层面来讲,弹性计算可实施性非常低。
问题二:能否使用CDN技术缓存Card资源?
Card 资源为后台动态生成,有些 Card 是无状态的,有些是有状态的,CDN 网络可以处理无状态的 Card 资源,对于有状态的,目前还没有成熟的 CDN 解决方案,同时 Card 资源的使用方式也与传统的 CDN 资源使用方式不太相同。
技术方案
针对这种每天粒度周期性爆发的业务模型,团队总结出三种技术方案。
方案一:针对业务的定制化方案
这种方案分析业务特点,提出解决方案,不具有通用性,目前平台的 Card 服务使用方包含各个端的 Feed 组以及通信系统,而周期性爆发流量,目前仅有通讯系统中的群发和群聊会产生,针对这种情况,Card Service 改进点:
- 区分有状态和无状态的 Card 请求,有状态的不缓存。
- 无状态的 Card 请求,结合业务特点,判断是否是群发以及群聊业务。
- 对于群发和群聊业务,将 Card 资源缓存在分布式缓存中。
分布式缓存有一个问题,虽然可以按照服务号水平分区在不同的节点,克服缓存容量问题,但是不能处理瞬时的千万级流量高峰,这种情况需要在每台前端机上做本地缓存,大小受前端机内存大小限制,我们根据业务特点进一步做优化, 对于群发和群聊业务,使用 L1 本地缓存和 L2 分布式缓存。
- L1 本地缓存处理服务号粉丝规模超过 20 万的,通过众多的前端处理机 L1 缓存抗峰值。
- L2 分布式缓存处理粉丝规模较小的服务号,处理从 L1 缓存穿透过来的访问。
方案二:简单的数据挖掘策略
通用方案基于实时的流量统计分析,运用数据挖掘算法,统计出资源请求中最频繁的 K 个,同时本地缓存大小不能太大,计算性能要求也较高,方案二将高频资源请求检测模块放在前端机本地缓存,使用类蓄水池采样算法,适合微博的群发场景。
这里介绍一下蓄水池问题:在不知道文件总行数的情况下,如何从文件中随机的抽取一行?
首先想到的是我们做过类似的题目吗? 当然,在知道文件行数的情况下,我们可以很容易的用 C 运行库的 rand 函数随机的获得一个行数,从而随机的取出一行,但是,当前的情况是不知道行数,这样如何求呢?我们需要一个概念来帮助我们做出猜想,来使得对每一行取出的概率相等,也即随机。
有了这个概念,我们便有了这样一个解决方案:定义取出的行号为 choice,第一次直接以第一行作为取出行 choice ,而后第二次以二分之一概率决定是否用第二行替换 choice ,第三次以三分之一的概率决定是否以第三行替换 choice ……,以此类推。这种方法的巧妙之处在于成功构造出一种方式,使得最后可以证明对每一行的取出概率都相等。
微博业务场景中的问题为长度为 K 的蓄水池,当 Card 资源没有命中缓存时,资源以 K/N 的概率存储到缓存,其中 K 为蓄水池的长度,N 为时间片内的平均访问量。
方案三: 基于 Spark 集群的 Top K 资源请求
这是方案二的升级版,将高频资源请求检测模块从本地缓存迁移到 Spark 集群,通过其强大的数据处理能力,更精确更实时地统计 Top K 高频资源访问。
目前,工程领域已经涌现众多性能优越的 Top K 算法,有基于计数的 Sticky Sampling 算法,也有空间使用率很低的 Space Saving 算法(思路和蓄水池有一点像,都是概率替换),还有使用 Bloom Filter 数据结构的算法,这些算法作用于 Spark 集群,理论上效果比方案二更好,但是消耗的资源更多。
总之,文中的三种方案没有好坏之分,应该结合公司业务特点,选择最适合的。
本文首发于“微博平台架构”微信公众号,发布时有少量的文字润色和调整。
关于作者
卫向军( @卫向军 _ 微博),毕业于北京邮电大学,现任微博平台架构师,先后在微软、金山云、新浪微博从事技术研发工作,专注于系统架构设计、音视频通讯系统、分布式文件系统和数据挖掘等领域。
感谢臧秀涛对本文的审校。
给InfoQ 中文站投稿或者参与内容翻译工作,请邮件至 editors@cn.infoq.com 。也欢迎大家通过新浪微博( @InfoQ )或者腾讯微博( @InfoQ )关注我们,并与我们的编辑和其他读者朋友交流。
评论