如何实现一个直播APP

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/WSRspirit/article/details/81589048

写在前面

直播从16年兴起到目前的趋于稳定,期间APP市场上出现过多个直播产品,史称“千播大战”,我有幸参与其中的一款产品(腾讯NOW直播),并经历了从上线到优化再到探索的整个过程。因为是在腾讯所以这款产品的开发有着很深的腾讯印记,我不想来介绍NOW是怎么设计和实现的,我想跳出来讲讲,如果我是这款产品的负责人,那么我要怎么设计,我会怎么实现,我要怎么玩。

总体结构

技术上面分为后台,客户端和web。

在初期设计阶段一定要注意的一点是:android和ios的实现统一。android的特点就是想怎么玩都可以,android基于了开发者无限的权利,但是ios往往受限于各种苹果的政策,所以两端在方案设计初期一定要确定好实现方式,如果不能统一用native那就统一用web,实现不一致的事情迟早都是给自己埋坑。

web的优点就是通用,但是缺点也很明显,速度和安全。

web和native是两个很关键的选择,大致讲注重通用性选择web,注重体验选择native。另外一个关键点是,web可以随时发布,客户端则要有更新周期,在遇到大的bug时,android还能热补丁,ios就很难受了,web则更灵活。如何选择是一门艺术,但切记不要拍脑袋决定。

有了web和客户端,那么对于后台来说我们就要实现两种协议支撑了,http和tcp。

后台的整体架构分为三层,分别为接入层、逻辑层和存储层。如图:

这里写图片描述

接入层

何为接入层,在流量真正接入到我们业务后台之前,提供就近接入,安全策略、ip收敛和负载均衡等功能,http协议解析之后统一负载均衡的打到后台的nginx中,后续就是逻辑处理了。对于tcp请求,在移动端的复杂网络条件下很多问题都很难处理,业内已经有很多成熟的SDK,如微信mars和网易的云信。我们借助这些SDK完成网络通信,这样做解放了客户端同学的人力,毕竟在复杂网络中做好连接的维护不是一件简单的事情。同时后台对于tcp请求统一接入到一个代理服务中,再有其进行转发,后台隐藏起来方便其内部进行流量控制调整,请求调用链分析,服务质量监控等。

接入层往往逻辑很简单,就是反向代理,再有一些登录态、安全和消息不全的基础功能,无论tcp还是http都要将请求打到逻辑层的业务中。如何标识一个请求对应的服务就需要命令字了,在阿里一个命令字就是java类的全称,子命令字则是具体的方法名,这也是很多java的rpc框架实现的基准。但是上述的实现是需要服务注册、消费等一整套基础框架支撑的,大多数互联网公司都有这个能力,但是腾讯欠缺,因此我们的实现往往最接近事情原本的样子,一个命令字往往是一个16进制的数字或者一个字符串,字符串要比数字更清晰明了。

协议的设计选择pb,因为更高效的编解码效率和简单易懂的pb文件,一个清晰的pb文件能够少写一篇wiki,甚至能够瞬间了解整个流程,但http层通常是json格式。json到pb可以无损失的转换,但pb到json则会丢失精度,原因是byte和long。

那么在逻辑层实现之前,我们还要有个能够处理tcp和http的框架。我自己使用netty+spring+protostuff实现了一个名为Teresa的框架,并且还会加入quasar协程库,我会在另外一篇文章分析一个rpc框架和一套微服务系统的设计要领。

逻辑层

对于直播业务的逻辑层,分析一下最重要的三个系统。

直播间

直播当然需要有直播间了,基本上所有的直播场景都是围绕着直播间运转的。直播间有两块相互独立的系统,一个是音视频一个是直播间具体的业务,进入直播间是一个相对重的逻辑(白名单,黑名单,特效,排行榜等众多业务),进入直播间业务失败也不应该让用户看不到音视频流,因此音视频流和直播间应该是解耦的,直播间的开播和音视频的推流相互不影响,这样方便音视频流的测试而不影响直播间的具体业务。

这里写图片描述

直播间是整个直播业务的核心链路,平时明星活动时一个直播间上10w真实用户都有可能,挑战点有两个:

  1. 推还是拉。房间是一个消息内容的承载,消息的获取无非就是推push或者拉pull,push的好处是实时性强,缺点就是需要维护成员列表,还有需要关注linux单进程的连接数限制。pull的好处是没有成员列表,缺点是实施性差,请求可能是锯齿状的,错峰能力需要提前考虑,否则有可能瞬间所有客户端一起请求,压垮后台。一个复杂的系统往往是推拉结合的,push可以消息合并,pull可以兜底。那么一个问题就是成员列表,如何把10w+的用户成员列表维护好,mysql肯定不行,用redis、tair或者ckv之类的缓存?性能方面基本上已经到这类缓存的瓶颈了,况且有同时开播的直播间会有1k+,再者网络抖动带来的影响很大。那就只有内存了,内存的问题是怎么把同一个直播间的用户分配到一台机器上,一致性hash,使用内存的缺点就是数据易丢失,直播间的场景我们不需要考虑持久化,只需要在直播开始到直播结束保持稳定即可。但一台机器还是会有单点问题,那就多写,多机完全冗余,并且能够分担读请求压力。多机的问题就是一致性扩容,客户端在进入直播间之后需要定时发送业务心跳来维护在线状态,同时可以返回一些业务信息,1/min的业务心跳能够保证多机中成员列表多数情况一致。如果多机仍然扛不住读请求,那么就需要水平扩容,使用内存的另一个弊端就出现了,如何数据搬迁,搬迁时的变化又怎么处理?还是依赖于心跳,水平扩容先写,过2分钟后基本上就可以提供读了。在日常的直播场景中,小直播间(1w以下)占绝大多数,基本的业务都或多或少依赖成员列表。但是去年百万英雄之类的火爆答题(100w同时在线)条件下,成员列表就没必要存在了,另外那个场景另外再分析。
  2. 怎么推怎么拉。进/退直播间、直播间内的心跳、送礼、发言等等都是直播间内的消息,这多多消息怎么处理。肯定要合并写、合并读,合并是根据直播间id进行合并的,合并的载体依旧是内存,那就又需要一致性hash了。另外一个问题就是成员列表怎么传输,分页拉取,增量调整。

pk赛

pk就是两个主播比赛,通过送礼来加分,分数多的人胜出,是营收的主要来源,比赛方式多种多样,主要就是为了让用户看直播也有一种追星的仪式感。
这里写图片描述

PK的主要挑战是以下三点

  1. 匹配。玩过dota,lol的都知道这个流程,在当时12年左右11对战平台很火的时候,匹配效率最高的时候都能到毫秒级,那应该是一个很牛逼的系统。匹配的算法我们使用非常经典的EL0,也可以根据场景再改变。当用户点击pk时,将用户放入redis队列,根据时间变化,动态调整匹配上下限,直到成功为止。很简单的问题就是多机情况下的事务性,即不能把一个人同时分配了两场pk。如果用户量小,你甚至可以两台机器抢一个用redis实现的锁,抢到锁去执行匹配流程,但是匹配的瓶颈也就是单机的计算能力了。两阶段提交,把匹配出来的用户,放到第二个redis set队列中,如果重复就把用户重新放到原本队列中继续匹配。但是极端情况下会出现一直冲突,进入死循环。那就将等待队列分级,匹配冲突之后放到优先级更高的队列中,主动减少冲突。这种实现方式也应该是dota2的实现方式。11对战平台肯定不会这么实现,redis队列能否抗住和在鱼塘(大多数菜鸟玩家)分数段的匹配冲突都是两个问题。那只能再次细分排队队列,然后直接使用内存存储用户分数信息。扯远了。
  2. 比赛。送礼事件通过mq旁路到专门的分数计算服务,分数在通过直播间广播到用户,在依赖于直播间的合并读写push时,业务也需要主动合并消息,减少网络流量,推送分数要推送全量而不是增量。实现时通过进程缓存能cache住大部分的请求,收益很高。
  3. 结算。粉丝喜欢搞大事情,总是喜欢在最后几秒钟送大礼物,结算时就会有很多问题,比如服务器的时钟漂移,解决方案依旧是延后处理,两阶段提交。首先页面上展示结算中,给后台主动争取一些时间,然后将结算时间向后+buffer。胜利之后还会给粉丝一些礼物可以抢,这就是一个类似秒杀的场景了,限频、限流、过载保护非常重要。

答题

17年年末答题火爆,只要是直播产品就一定会来蹭一波热度。答题的场景和特点很明了,人多,钱多。人多动辄100w+,钱多也是100w+。答题场景我们放弃了所有其他边缘业务,只做一件事就是答题,但还是有以下问题:

  1. 人数众多。这么多人怎么搞,依然是一致性hash,但是每台机器是单点,没有备机,因为业务心跳也没了。如果答题时宕机就会影响一部分用户飘逸到其他机器上,这个用户的资料就丢失了,我们通过把用户资料传给客户端,客户端请求时主动带上来,这样我们就有一份兜底数据。安全吗?相对安全,因为只有飘逸时才使用兜底数据,并且对数据加密(非对称)。基本上不可能用户一直在多台机器漂移。那么用户会不会有ABA问题,即宕机的服务恢复了,然后又飘回来了,因为原有机器上数据存在,但是之前的老数据,会不会有影响。解决方案:时间戳。
  2. 峰值。8点开始的答题,不可能在一瞬间完成,业务需要主动的通过提前通知用户,引导错峰进入直播间,八点主持人再叨叨一会儿,给业务多点时间。
  3. 拉。没有了成员列表,push变得不可能,拉的问题就凸显了,pull不能集中(后台可以控制请求频率,将请求打散),2000-3000ms。
  4. 时间。答题一般10s,音视频延时时间+网络延时时间,计算一个最大的答题时间。
  5. 题目。题目混在音视频流中容易跟着一起卡,因此需要题目单独拿出来。
  6. 异地部署,就近接入。上海、深圳和天津依照公司的组件完成就近接入。
  7. 结算,在结束后需要将得奖用户写入redis,最终在将三地的redis中用户汇总(mb级别),再通过mq异步发奖。

存储层

存储层在2c业务方面起到一个很重要的角色,mysql这种传统的关系型数据库有可能在性能方面扛不住,redis在性能方面没有什么问题,但是需要考虑缓存击穿问题,而且这个问题还不太好处理,需要考虑的问题还很多。腾讯的ckv在这方面做得很好,一主一备,自动扩容,lru冷备,完全就当做能落地的持久层用。redis会受到单机容量限制,codis虽然搞定了这个问题,但是key的分片是一个坑,用不好坑自己。同时redis在容量满时会有丢弃策略。想用好需要下点功夫。

产品方面

直播是一个变现十分可观的业务,相比于游戏直播,mm直播收入更高,同时带宽要求更低。但是直播也需要大流量一直在养,像抖音通过短视频养,momo通过陌生人社交的基数养。
但是直播也能带来更多的玩法,比如答题、抓娃娃、k歌房、1v1、桌游(谁是卧底、狼人杀)等。做产品也要大胆的给新兴业务一些浅入口,高曝光,而不是藏在角落,这可能涉及到产品册的一些思路。仅仅通过pk,各种活动来榨干用户的血是一种竭泽而渔的做法。要么就是看似直接但是套路很深的红包只能带来一些表象的DAU上升。
直播间是一种能力,它其实赋予了我们原本在线下开展的活动到线上的能力,就比如答题活动。在开放思路,原本的《幸运52》,是不是也能改变一下,或者非诚勿扰。玩法的升级,使得自身能够有能力吸引更多流量。

写在后面

业务到了技术永远都是万变不离其宗,对于后台,客户端,web都有很多的通用之处。分析其中的关键点

设计艺术

  • 能力收敛,以接口形式提供服务
  • 责任划分,整个流程清晰可见
  • 前端保护后端,上游保护下游,各端互不信任

猜你喜欢

转载自blog.csdn.net/WSRspirit/article/details/81589048