极客时间已完结课程限时免费阅读

10 | 自动智能扩缩容:直播互动场景中峰值流量的应对

10 | 自动智能扩缩容:直播互动场景中峰值流量的应对-极客时间

10 | 自动智能扩缩容:直播互动场景中峰值流量的应对

讲述:袁武林

时长16:02大小14.68M

你好,我是袁武林。
随着近几年各种直播 App 和百万答题 App 的火爆和风靡,具有高实时性要求的直播互动场景开始纷纷借助即时消息技术,来保证直播过程中的各种互动消息和行为能够及时、可靠地投递,比如用户给主播打赏或者送礼的互动行为,不能有超过 10 秒的延迟,更不能丢失,否则会导致主播和房间其他用户看不到。即时消息技术凭借其在实时性和可靠性方面的优势,已经被广泛应用在互动直播场景中。
那么,和传统的消息聊天场景相比,直播互动在业务形态上究竟有哪些区别?在技术层面又有哪些高难度的挑战?

业务形态区别和技术挑战

首先,在业务形态上,与传统的即时消息场景不太一样,直播互动的流量峰值具有“短时间快速聚集”的突发性,流量紧随着主播的开播和结束而剧烈波动。
另外,直播互动是以房间为单位,传统的群聊业务和聊天室业务虽然也有千人群和千人聊天室,但和直播间动辄几十万、上百万人的规模相比还是小巫见大巫的。而且,直播互动由于房间有时效限制和明星效应,用户发言和互动的积极性会更高,毕竟可能“错过这村就没这店了”。
超大的房间规模及高热度的互动导致的一个问题,就是消息下推的并发峰值。这里我们可以简单用数字来直观感受一下:点对点聊天场景,如果两个人每 10 秒说一句话,实际上每秒的消息下推数只有 0.1;群聊或者聊天室场景,假设是一个 500 人群,如果群里每个人也是每 10 秒说一句话,实际每秒的消息下推数是 500 / 10 * 500 = 25000;那么对于一个 10w 人在线的直播互动场景,如果直播间里每个人也每 10 秒说一句话,实际每秒可产生的消息下推数就是 100000 / 10 * 100000 = 10 亿。
当然,这里只是用这个例子计算一下理论值,来让你了解直播互动中的并发压力与普通聊天场景的区别之大。
实际上,10 万人的直播间一般不会有这么高的发言和互动热度,即使能达到,也会在服务端进行限流和选择性丢弃。一个是考虑服务端的承受能力基本不可能达到这个量级,另一方面,即使消息能全部推下去,客户端也处理不了每秒一万条消息的接收,对客户端来说,一般每秒接收几十条消息就已经是极限了。因此,由于业务形态的不同,直播互动中的高并发挑战与传统的即时消息场景相比要大得多。

直播互动的高并发应对

对于直播互动中高并发带来的技术挑战,我们从架构层面来看有哪些应对手段呢?下面我们先来分析一下直播互动中的一个比较大的挑战:高并发压力。

在线状态本地化

实际上,直播互动中的并发压力主要来自于消息下推环节中消息从一条扇出成十万条后的那部分,消息扇出前相对压力并不大。
那么,我们的优化重点主要是在扇出后的逻辑上,对于普通的消息聊天场景,扇出后的推送逻辑主要是:“查询聊天接收方在哪台接入服务器,然后把消息投递过去,最后由接入服务器通过长连接进行投递。” 如果采用这种方式来处理直播互动的消息下推,大概的流程会是下图这样的:
首先,用户通过接入网关机进入直播间;接着,网关会上报用户的在线状态;假设这时用户 A 发送了一条弹幕消息,这条消息会在业务逻辑处理层进行处理,并且业务逻辑处理层通过查询刚才维护的用户在线状态,会相应地查询用户 A 所在直播间的其他用户都在哪些网关机上,并把相应的消息投递到这些用户所在的网关服务器;最后再由网关服务器推送给用户的设备。
这里存在的一个问题是,在普通的聊天场景中,为了进行精准投递避免资源浪费,一般会维护一个中央的“在线状态”,逻辑层在确定好投递的接收人后,通过这个“在线状态”查询对应接收人所在的网关机,然后只需要把消息投递给这台网关机就可以了。
但是对于直播互动场景来说,如果采用这种模式,一个 10w 人的房间,每条消息需要对这个在线状态进行 10w 次查询,这个量级是非常大的,因此往往这个地方就会成为瓶颈。
那么针对直播互动场景,对于这个“精准投递”应该如何进行优化呢?我们一起来思考一下。
一般来说,即使是一个热度较大、在线人数几十万的直播间,房间里的用户实际上也是“无状态的”相对分散在多台网关机上。
以 10w 人的房间来说,假设有 50 台网关机,那么平均每台网关机上这个直播间的用户应该有 2000 人,我们完全没有必要去“精准”确认这个直播间的用户都在哪台网关机上,只需要把这个直播间的消息都全量“投递”给所有网关机即可,每台网关机也只需要在本地维护一个“某个房间的哪些用户的连接在本机”,最终由网关机把消息下推给本机上当前直播间的在线用户。优化后的直播消息下推架构大概是这样:
首先,每一台网关机在启动时会订阅一个全局的消息队列;当用户进入直播间后,会在每台网关机的本机维护一个在线状态;同样的,假设这时用户 A 发送了弹幕消息,这条消息会在业务逻辑处理层进行处理;紧接着再由业务处理层投递给刚才网关机订阅的全局的消息队列,这样所有网关机都能收到消息;最后,每台网关机根据本机维护的某个直播间的在线用户,再把消息下推给用户设备。
通过这个优化,相当于是把直播消息的扇出从业务逻辑处理层推迟到网关层,而且扇出后的下推不需要依赖任何外部状态服务,这样就能大幅提升直播互动消息的下推能力。
至于直播间里极少数的点对点类型的消息扇出下推(比如主播对某个用户禁言后下推给这个用户的提醒消息),可能会有一定的资源浪费,但这类消息数量相对较少,整体上看收益还是比较大的。

微服务拆分

对于直播互动的高并发场景来说,仅仅有架构和设计层面的优化是不够的。比如,下推消息还受制于网关机的带宽、PPS、CPU 等方面的限制,会容易出现单机的瓶颈,因此当有大型直播活动时,还需对这些容易出现瓶颈的服务进行水平扩容。
此外,为了控制扩容成本,我们希望能够区分出直播互动场景里的核心服务和非核心服务,以进一步支持只针对核心服务的扩容。同时,对于核心服务,我们需要隔离出“容易出现瓶颈点的”和“基本不会有瓶颈的”业务。
基于这些考虑,就需要对直播互动的整个服务端进行“微服务拆分”改造。
首先,我来分析一下对于整个直播互动的业务来说,哪些是核心服务、哪些是非核心服务。比如:发弹幕、打赏、送礼、点赞、消息下推,这些是比较核心的;其他的如直播回放和第三方系统的同步等,这些业务在直播时我们是不希望干扰到核心的互动消息和行为的收发的。
除此之外,在核心服务里,消息的发送行为和处理一般不容易出现瓶颈,一个 10w 人的直播间里每秒的互动行为一般超不过 1000,在这一步,我们不希望和容易出现瓶颈的消息下推业务混在一起。因此,我们可以把消息的发和收从接入层到业务处理层都进行隔离拆分。整个系统进行微服务化改造后大概就是下面这样:
核心服务通过 DB 从库或者消息队列的方式与非核心服务解耦依赖,避免被直接影响;容易出现瓶颈的长连接入服务独立进行部署,并且和发送消息的上行操作拆分成各自独立的通道,这样一方面能够隔离上行操作,避免被下行推送通道所影响,另一方面,轻量、独立的长连接入服务非常便于进行扩容。

自动扩缩容

通过微服务拆分后,你就需要考虑如何对拆分出来的服务进行扩容了,因为在平时没有高热度的直播时,考虑到成本的因素,一般不会让整个服务的集群规模太大。当有大型直播活动时,我们可以通过监控服务或者机器的一些关键指标,在热度快要到达瓶颈点时来进行扩容,整个过程实际不需要人工参与,完全可以做成自动化。
对于直播互动场景中的监控指标一般可以分为两大类:
业务性能指标,比如直播间人数、发消息和信令的 QPS 与耗时、消息收发延迟等;
机器性能指标,主要是通用化的机器性能指标,包括带宽、PPS、系统负载、IOPS 等。
我们通过收集到的业务性能指标和机器性能指标,再结合模拟线上直播间数据来进行压测,找出单机、中央资源、依赖服务的瓶颈临界点,制定相应的触发自动扩缩容的指标监控阈值。
大概的自动化扩缩容的流程如下:

智能负载均衡

了解了自动扩缩容的整体流程,还有一个在扩容中需要你关注的问题。
对于直播互动的消息下推来说,长连接入服务维护了房间和用户的长连接,那么这里的问题在于:扩容前的机器已经存在的长连接可能已经处于高水位状态,新扩容的机器却没有承载用户连接,而对于长连接入服务前端的负载均衡层来说,大部分都采用普通的 Round Robin 算法来调度,并不管后端的长连接入机器是否已经承载了很多连接,这样会导致后续新的连接请求还是均匀地分配到旧机器和新机器上,导致旧机器过早达到瓶颈,而新机器没有被充分利用。
在这种情况下,即便是让负载均衡层支持自定义的复杂的均衡算法,也可能无法解决流量不平衡的问题。因为很多情况下,负载均衡层本身也是需要扩容的,自定义的均衡算法也只能在某一台负载均衡机器上生效,无法真正做到全局的调度和均衡。
一个更好的方案是接管用户连接的入口,在最外层入口来进行全局调度。
比如,在建立长连接前,客户端先通过一个入口调度服务来查询本次连接应该连接的入口 IP,在这个入口调度服务里根据具体后端接入层机器的具体业务和机器的性能指标,来实时计算调度的权重。负载低的机器权重值高,会被入口调度服务作为优先接入 IP 下发;负载高的机器权重值低,后续新的连接接入会相对更少。
通过这种方式,我们就基本能解决旧机器和新机器对于新增流量负载不均衡的问题了。

小结

接下来我们简单回顾一下今天课程的内容。今天这个章节我主要从直播互动的场景出发,先带你从业务维度了解到直播互动相对普通聊天具有“突发流量”“超高下推并发”等特点。为了应对这些问题,我们可以从几个方面来进行针对性的优化。
首先,在线状态本地化维护,降低远程资源依赖,提升单机处理能力。
其次,对服务整体进行拆分,区分核心和非核心服务,隔离“容易出现瓶颈”的服务。
接着,通过收集业务和机器两类指标,建立容量评估模型,自动进行服务扩缩容。
最后,根据后端机器负载水平调度全局,接入服务入口,解决扩容后新接入流量在新扩容机器和旧机器间流量不均衡的问题。
如今,自动扩缩容作为互联网公司标配的平衡服务处理能力和资源成本的基础设施,特别是对于流量峰值波谷明显的业务,带来的收益非常明显。虽然我们今天站在直播互动的场景来展开这个话题,但这个技术其实不仅仅可以用于 IM 场景,也具有很强的业务普适性,希望你能从中有所收获和启发。
最后给大家留一道思考题:通过长连接的接入网关机,缩容时与普通的 Web 服务机器缩容相比有什么区别?
以上就是今天课程的内容,欢迎你给我留言,我们可以在留言区一起讨论。感谢你的收听,我们下期再见。
分享给需要的人,Ta购买本课程,你将得18
生成海报并分享

赞 12

提建议

上一篇
09 | 分布式一致性:让你的消息支持多终端漫游
下一篇
11 | 期中实战:动手写一个简易版的IM系统
 写留言

精选留言(39)

  • 饭团
    2019-09-18
    长链接在服务器缩容的时候需要做: 1)需要告诉网管机,自己准备停止接入新的链接! 2)在最后一个链接断掉后后才能退出 短链接 和 长链接比 变化实效性要高很多

    作者回复: 是的,实际上也不需要等到最后一个长连断开哈,对于剩余的少量的长连接可以采取强制断开方式,等待客户端断连重连即可。

    共 2 条评论
    20
  • 2019-09-18
    老师,直播回放是如何保持播放进度与聊天内容在时间上同步的,用什么技术实现的

    作者回复: 可以通过插帧的方式来解决。视频流每隔一定帧数内,插入服务器的时间戳,聊天内容也埋入服务器的时间戳。回放视频时到达相应的时间戳,获取跟这个时间戳相匹配的消息在页面上渲染出来即可。

    19
  • 淡蓝小黑
    2019-09-26
    文中提到【通过这个优化,相当于是把直播消息的扇出从业务逻辑处理层推迟到网关层】和 您回复我的【业务层在把消息扇出给到网关机时,就已经具体到接收人了,所以不需要通知网关机来进行变更。】 这两条不冲突吗?(我看唯一的区别是文中说的是直播间场景,您回复我的是群聊场景,还是说群聊和直播间是用不同的模式扇出消息的?),其实我想问的是,用户加入直播间,网关机本地维护【本机某个直播间有哪些用户】这个数据的话,当用户离开直播间,加入另一个直播间时,业务处理层还要通知网关层更新本地维护的那个数据,有可能会出现数据不一致的情况,导致用户加入新直播间了,但由于网关层数据没有更新,用户收到不到新直播间的消息。
    展开

    作者回复: 嗯,这是个好问题,我们来理一理。 先想一想群聊和直播的使用场景,对于直播场景来说,房间和房间之间是隔离状态,也就是说每次用户进入到一个房间,都需要通过信令告诉网关机自己当前连接的是哪个房间,这样网关机才不会推错消息。正是通过这个加房间的信令,网关机有机会来维护一个 房间 - 用户 -连接的关系,因此对于直播间消息,我们的消息给到网关机时只需要按房间维度来pub消息就可以;但是对于群聊来说,群和群之间不是隔离状态,我们的长连接对于任何群的消息都是需要推送的,因此在app打开建立好长连后,客户端不需要再告知自己当前进入了哪个群,所以网关机在建立长连时只能建立一个 用户 - 连接的映射,所以对于群聊我们需要在提交给网关机时扇出成用户维度才可以。 理解了上面说的就不难解答你的问题,用户离开直播间加入另一个直播间都会通过长连通道下发一个退出旧直播间信令和加入新直播间信令(或者一个切换房间信令),这多个信令都是被长连接那一台网关机接收到并处理的,所以不存在需要多网关机同步数据的问题哈。

    共 2 条评论
    11
  • yic
    2019-11-15
    老师,为了避免每条消息都查询用户的在线状态,所有的消息都发送给所有的网关节点,这样也会造成每台网关机器的流量成倍数增长吧。这样,是不是会影响消费者推送消息的速率呢?毕竟,如果有50台网关节点,原来每台网关节点只需要取1条消息,现在却需要取50条消息,其中有49条是无效的。

    作者回复: 是的,所以这个需要一个权衡,如果业务场景大部分都是点对点场景那么使用全局在线状态来精确投递是更好的选择,如果是群聊和直播类似扇出较大的场景推荐使用所有网关来订阅全量消息的方式。

    共 2 条评论
    6
  • 王棕生
    2019-09-24
    通过长连接的接入网关机,缩容时与普通的 Web 服务机器缩容相比有什么区别? 答: 普通的Web服务器机器提供http的短连接服务,缩容时拿掉机器,会导致前端连接失败,但通过nginx的负载均衡算法,会使重连的客户端连接到另外一台服务器上,这对客户端来说,基本是无感知的; 但是长连接的接入网关机,在缩容拿掉机器时,会导致这台机器上的所有的长连接全部断掉,此时是会影响到所有连接到这台网关机的所有用户,当然通过入口调度服务,客户端可以通过重连连接到新的网关机上,但是用户的体验始终是不好的。
    展开
    共 2 条评论
    5
  • 卫江
    2019-09-18
    思考题:基于长连接与web的服务缩容的区别。本质的区别是长连接与短链接的问题,基于长连接就意味着服务器在内核中保存了一些连接状态,而为了更好的扩缩容保持服务的无状态是最好的,因为这些状态会在服务回收后消失,当然了基于web的服务,我们可能也会在应用层保存用户的session等信息,不过这一块可以放在外部存储,比如缓存,所以,基于长连接的服务缩容一定会造成连接信息的丢失,从而触发客户端断线重连以及建立长连接的整个流程。
    展开

    作者回复: 是的,对于长连接的网关服务,我们缩容是只需要禁止新的建连请求接入,已存在的长连接尽量等用户自动断开后关闭,对于剩余的少量的长连接可以采取强制断开方式,等待客户端断连重连即可。

    4
  • Derek
    2019-09-26
    对于高在线的房间,做全量网关转发是合适的,到对于低在线,极端情况就是2个人,这种方式有点浪费,而其实绝大部分大型直播平台,低在线占绝对比例。

    作者回复: 嗯,看具体业务的在线率和网关机数量吧。低在线直播间本来没量也不是重点,我们的重点是要解决那少数几个高热度、高并发的直播间的问题。

    3
  • 鱼向北游
    2019-11-05
    不知道老师还关注不关注这个留言,想问老师个问题,上面说的直播间那个把消息直接广播扇出过各个网关再由各网关来判断这个消息该推给哪个用户。感觉这个没法对网关做到水平扩容呀,因为即使扩容了,扩容网关所收到的消息也是全量的广播消息,压力一点都不会分摊,前阵子做过压测,用这种架构在原网关达到瓶颈后,新添加机器后,新添加的机器在没有用户连接的情况下光分拣消息判断消息不该发,这个操作已经占用到新机器70%的资源了,新机器承载不了多少新量,在这种广播模式下反而是用户都集中在某台或某几台机器上效果会更好
    展开

    作者回复: 扩容网关机收到的是扇出前的单条房间维度的消息呀,扇出是在网关机的逻辑里实现的,扇出完就推送出去了。分拣消息慢这一块可以优化一下,在用户上线时在网关机以房间维度建立当前房间的本地用户连接列表,下推时直接获取连接列表就可以了。我们线上下推qps实际能到千万级别,绝大部分机器都是弹性扩容的。

    共 3 条评论
    2
  • 2019-09-18
    听到老师在回复同学的“监控当前总连接数、每秒建连数、close_wait的连接数、Send-Q、Recv-Q、backlog队列、重传率、pps、带宽使用情况“,深感自己不足,tcpip协议详解这本书没啃下来,老师有推荐的有关网络的书籍吗

    作者回复: 个人感觉还是需要理论结合实际来学习哈,平时没事的时候可以用wireshark抓点包来分析研究一下,印象和理解都会不一样的。推荐一下林沛满的两本wireshark的书吧。。

    2
  • 晴天
    2019-09-18
    通过类似redis的pub/sub实现服务端与客户端长连接消息投递,和队列记录长连接的服务端ip对应客户端标识;这2中方式哪一种应用的更为广泛?

    作者回复: 直播和聊天室场景第一种使用更多,点对点的也有很多使用第二种的,对于网关服务器不太多的业务,个人倾向都使用第一种。

    2
  • 2020-02-18
    老师你好我有一个问题和想法: 消息扇出时,全量推送网关,这块能否继续优化? 我的想法是,客户端按直播间归类,相同直播间的客户在相同网关(需要考虑扩缩容),这样推送给网关时只需要推送网关所有直播间的消息。不知道是否可行?

    作者回复: 相同直播间的客户在相同网关的话需要考虑热点过于集中的问题,对于人数较多的直播间,这一块可能容易成为瓶颈。

    共 2 条评论
    2
  • 黄海
    2019-10-09
    老师, qq 上下线通知好友时,是要先查询好友们的在线状态以取得他们所连接的服务器,然后向这些服务器推送上下线消息吗? 从几亿人的在线状态数据中,查询出几百个在线好友,有什么优化手段吗?

    作者回复: 这个问题可以思考一下:一个用户的好友是有限的,在线状态如果是通过中央kv型存储的,并发查询几百个好友也并不是个问题,性能上不会太慢,只是存储压力会比较大。如果真要优化,好友数太多的情况下,我个人觉得可以把这个用户的好友查出后,组装成一条特殊消息下发给所有网关机,由各台网关机认领各自本机维护的这些好友中的那些在本机登录连接的,然后push上下线消息就可以。

    1
  • laolinshi
    2022-03-28
    老师,如果网关机使用的不是外网IP的话,那你文中提到的智能负载均衡服务提供给客户端的IP应该是LVS服务的IP了?
  • Sophia
    2022-01-04
    实际操作中, 直播间IM方案 pull 要比 push 好一些。
  • 橙子橙
    2020-10-26
    老师请教下 网关服务或者业务服务上下线时候,怎么保证消息不丢。 比如某个消息在业务服务刚处理完,在发送至网关的tcp了解路上,此时该网关服务重新发布,重启了,中间状态的消息是不是全丢了…
  • 鲁大喵
    2020-09-27
    下推到网关服务器去做这个操作确实会减少很多操作,但这样网关链接层可能和业务有那么一点点耦合,比较考验设计,如果经常发版导致客户端经常掉线其实不太好,不过感觉对于同一个业务来说问题不大。
  • Geek_f5a46e
    2020-09-20
    智能负载均衡有点不太理解: 这里的"入口调度服务",是怎么运作的? 两种猜测: 1.当调度服务发现某台新部署的机器负载低时,告诉客户端直连这个ip 2.由调度服务告诉nginx(不知道nginx是否有类似动态修改的接口),nginx调整权重,客户端还是通过nginx访问
    展开
  • 唯我天棋
    2019-11-04
    通过长连接的接入网关机,缩容时与普通的 Web 服务机器缩容相比有什么区别? web服务器扩容,只需要再http网关服务下线机器,不再把流量打过来就可以。 长连接网关发布,则需要多一个通知客户端断链重连的过程

    作者回复: 是的,一般还需要先禁止新的连接接入,可以通知客户端断线重连或者等待大部分旧连接断线后再杀进程。

  • wuhaka
    2019-11-01
    老师您好,基于这节内容我有个问题请教下,你这边IM集群负载均衡的方案和架构是怎样的?听文中意思好像客户端通过http接口拉一个负载低的ip然后直连,不知道我理解错没,或者是否有其他的负载方案,客户端只连一个固定ip就可以,请您分享下业界主流的一些方案,多谢。

    作者回复: 这个具体还是需要根据你的业务访问量来决定,一般单个域名搭配多个vip能满足大部分场景的,对于访问量很大、对DNS解析性能不满意的可以通过HTTPDNS方式来解析域名并根据后端vip的压力进行均衡和调度。

  • Ricky Fung
    2019-10-23
    web服务器在设计时都是无状态的 可随意扩容与缩容,接入层网关机器保存的有用户长连接 可以理解为有状态的,缩容时需要本机上的长连接都失效