如何打造一个高性能、高可用直播系统架构如何打造一个高性能、高可用直播系统架构
目前斗鱼直播系统每天有 20 亿 + 的请求量,并且在今年 3 月份,PDD 入驻斗鱼的时候还曾达到 40 万 + 的瞬时 QPS,同时
我们的服务增长到了上千实例的规模,在全国不同地区和不同机房进行了部署,提供给上百个内部业务方使用。总结下来,斗
鱼直播系统有三个特点——流量大、服务多、架构复杂。针对这些问题,要确保整个系统的高性能和高可用,对于技术开发团
队来说也是一个比较大的挑战。
高性能业务架构演进中的挑战与解决方案
三大挑战
早在 5 年前,我们和其他公司一样处于单体应用时期,主要使用“Nginx+PHP+Memcache+MySQL”,当时遇到最大的一个问
题,是如果一个用户进入到直播间访问 Memcache 的时候,如果刚好 Memcache 里面缓存数据失效了,那么请求就会穿透到
MySQL ,会对服务造成很大的压力。
所以从 2016 年开始,我们将 Memcache 换成了 Redis,将全量的直播间的数据缓存到 Redis 的内存缓存,解除服务对
MySQL 的直接依赖,同时还做了一些业务隔离:将业务进行了垂直拆分。保证了那个时期的服务稳定。
但是随着斗鱼体量的日益增长,请求量越来越大,我们没想到 Redis 也会成为一个新的瓶颈。所以从去年开始斗鱼着手对系
统做了一些改造,将 PHP 换成了 Golang,然后在我们 Golang 里面做了一些内存缓存池和 Redis 连接池优化,经过这一系列
的改造,目前斗鱼的直播系统无论是在性能上还是可用性上都有很显著的一个提升。
首先给大家介绍一下我们斗鱼的 Memcache 时期。在理想的情况下,当一位用户进入到我们直播间页面,请求会先到
Memcache,如果 Memcache 里面有缓存数据,那么就会直接将数据返给用户。但是通常情况下并不总是理想状况,我们现
在举一个具体案例进行介绍。
假设有一位大主播开播了,他就会发开播信息给他的粉丝们,这些粉丝会在短时间内进入到该主播的直播间,这样就带来第一
个问题——瞬时流量。 紧接着大量请求会并发到 Memcache 里,由于 Memcache 采用是一致性 hash 的算法,所以同一个直
播间的缓存 key 会落在同一个 Memcache 节点上,这就会造成某个 Memcache 节点在短时间内负载过高,导致第二个问题
——热点房间问题。同时又由于直播间刚开播,之前没有人访问过这个页面,Memcache 里是没有这个直播间信息,那么又
会有大量的请求穿透到 MySQL,对 MySQL 造成性能影响,所以就造成了第三个问题——缓存穿透问题。
解决之道
针对上述三大挑战,我们做了一些优化。
首先要解决的是缓存穿透问题。斗鱼直播系统里写操作都是主播进行触发,而大量读服务接口则是用户进行触发,所以我们在
业务层面上做了一个读写分离,规定:只有主播接口才可以对 MySQL 和 Redis 做写入操作;并且将直播间基础数据全量且不
过期的缓存到 Redis 里。这样就去除了用户请求对 MySQL 的依赖。也就是说用户请求无论如何都不会穿透到 MySQL。
针对于第二个问题——瞬时流量,我们主要在 Nginx 的配置上面做了一些优化。首先,将直播系统单独分配一个 nginx 的
proxy cache,做一些隔离,避免非核心业务的 cache 占用了核心业务 cache 的空间;其次,对突发流量,在服务返回
502,504 状态码(服务容量不够了)时,返回上一次缓存的数据,避免直播间白屏,保证其可用性;最后,做了 proxy lock
on 的配置,确保瞬时同样的请求,只会有一个请求到后端,降低后端和数据源的负载。
最后针对热点房间问题,我们将一致性 hash 的 Memcache 换成了主从结构的 Redis。多从库的 Redis,每个里面都有大主播
的数据信息,可以有效的分担大主播流量,从而使得后端数据源的负载均衡,不会出现单节点的性能问题。
新的问题