问题:
先回顾一下【直播问答系统】的需求分析和架构设计。
【直播问答】是2018年各大互联网平台兴起的一种吸引流量的运营活动,它是在视频直播的基础上增加了答题的玩法,具体流程见下图。

【视频推流】是视频直播;【题目下发、题目作答、答案及统计数据下发】是针对一道题目的三次交互过程,12道题目需要有12次循环过程;12道题目完成之后,最后才会【支付奖金】。
基于【解耦】和【减压】的思路设计【直播问答系统】,其系统架构见下图。

【直播问答系统】包括三个子系统:“直播子系统”、“答题子系统” 和 “支付子系统”;【直播问答系统】通过多通道方式实现客户端与服务端之间的交互:【直播通道】由 “直播子系统” 进行实现;【下发通道】和【上传通道】由 “答题子系统” 进行实现;【支付子系统】是直播完成后系统的一个异步化行为。
在【直播问答系统】架构中,【答题服务】是功能逻辑最复杂和压力负载最大的服务,也是作为架构师需要考虑的核心问题所在;我们今天的问题就是【直播问答系统】关键的四个核心问题应该如何解决和落地?
1. 判题逻辑如何实现?
【答题服务】怎样对用户提交的答案进行快速判断对错呢?要知道,用户虽然有10秒的作答时间,但通常提交答案集中在4~9这5秒内,即5秒时间内有500万用户的答案进行提交。
2. 淘汰用户如何识别?
已经作答错误的用户,不能继续进行答题,如何方便标识和快速识别出已经淘汰掉的用户呢?
3. 如何防止用户作弊?
常见的用户作弊手段有:(1)超时未作答,即用户在当前题目中因为时间超时没有作答,但在下一道题目中提交了答案;(2)答错时继续作答,即用户已经答错一道题目,但该客户端用户还在继续作答;(3)多次提交不同答案,即用户针对每一道题目,都分别提交所有答案?
4. 怎样快速统计数据?
每一道题目下发到客户端后,随着用户提交作答,【答题服务】需要快速统计出每道题目的作答情况:多少人选择了A,多少人选择了B,多少人选择了C,多少人答对,多少人淘汰。
解析:
我们先分析一下500万用户同时在线时,系统架构的扩展性。
-
【直播子系统】的实现通过集成第三方插件来落地,在选型调研时,需要做一下压力测试,避免并发风险;
-
【答题子系统】的 “题目服务” 在人工触发下用于下发题目和下发答案及统计数据,“题目服务” 本身没有任何负载压力,部署两个 “题目服务” 节点避免单点即可;下发数据的压力在 “Entry” 节点上;每一个 “Entry” 节点 如果可以承受 20万(根据服务器配置和实际压测效果来评估)客户端的同时在线和下发数据,那么500万用户则至少需要 25 个 “Entry” 节点 (实际则可能需要部署30~40个节点);
-
【答题子系统】的 “答题服务” 需要处理用户提交答案时的业务逻辑,这是整个系统最复杂,也是负载最高的服务,所以 “答题服务” 需要做无状态化处理,可以更加方便地横向扩展;
-
【支付子系统】的 “后台服务” 是异步化逻辑,对处理时间没有要求,所以负载压力一般;
-
【数据层的 Redis 和 MySQL】是整个系统架构的 IO 部分,决定了对接口的响应时间,需要我们在解决核心问题时优化对数据层的访问。
下面我们逐一分析【直播问答系统】的 【答题服务】是如何解决核心问题的!
一、判题逻辑如何实现?
即怎样对用户提交的答案快速判断对错。要知道,用户通常提交答案的时间集中在4~9这5秒内,即5秒时间内有500万用户的答案进行提交,每秒有100万次的请求;这要求【答题服务】必须具备很高的性能。有三种实现方案:
1. 前端实现方案
前端实现方案,即【答题服务】在下发题目之前,将题目答案预先下发到客户端;然后在用户提交答案时,完全由客户端来处理判题逻辑;见下图。

该实现方案下发题目答案的时机不好把控,最关键的问题是安全风险,毕竟题目答案下发到客户端后,服务端无法进行把控了。
2. 基于 Redis 实现方案
基于 Redis 实现方案,即题目答案缓存在 Redis 中;利用 Redis 的缓存特性,在每一个客户端提交答案请求时,【答题服务】 通过访问 Redis 来对用户提交的答案进行判断。

该实现方案应该是最常用的实现思路,在用户量规模不大的时候,是完全没有问题;在 500 万用户量级规模下, Redis 很容易成为性能瓶颈,因为每访问一次 Redis,就是一次网络 IO 的调用。
3. 答题服务实现方案
答题服务实现方案,即将题目答案从 MySQL 存储中直接读取到【答题服务】进程的本地内存;在用户提交答案请求时,【答题服务】直接从本地内存读取题目答案对用户提交的答案进行判断。

【答题服务实现方案】与【前端实现方案】对比,减少了答案暴露的风险;与【基于Redis实现方案】对比,减少了一次网络 IO 调用;总共12道题目的答案数据,占用内存空间非常小;我们最终是采用了 【答题服务实现方案】来落地判题逻辑。
二、淘汰用户如何识别?
已经作答错误的用户,不能再继续进行答题了,如何方便标识和快速识别出已经淘汰掉的用户呢?也有三种实现方案:
1. 基于 Redis 存储实现方案
基于 Redis 存储实现方案,即将所有用户的状态(0表示已淘汰,1表示未淘汰)全部存储在Redis中;在客户端用户提交答案时,【答题服务】通过访问 Redis 来判断当前提交答案的用户是否有继续作答的资格。

500万用户,可以通过 Redis 集群来存储,这不是主要问题;最关键的问题还是如前所示,Redis 很容易成为性能瓶颈,因为【答题服务】每一次访问 Redis 都是一次网络 IO 调用。
2. 答题服务存储实现方案
答题服务存储实现方案,是对基于 Redis 存储实现方案的优化,为了避免 Redis 性能瓶颈,将所有用户状态信息全部存储在【答题服务】进程的本地内存中。

将500万用户分散存储在不同【答题服务】节点内存,会使得【答题服务】失去 “无状态化”,此时 【答题服务】的高可用如何保证就会变得异常复杂。
3. 客户端存储实现方案
客户端存储实现方案,即将客户端每一次提交答案信息、复活币信息、状态信息等拼装成一个字符串,然后进行加密,成为一个只有服务端才能识别的 token 。客户端每一次提交题目答案时,都需要将 token 带上来,然后服务端解密后,加上本次提交的答案信息再形成一个新的 token。

客户端存储实现方案,是典型的用时间换空间的实现思路,仍然保持 【答题服务】的无状态化,没有 Redis 的网络IO调用瓶颈;同时加密和解密的密钥由服务端保管,安全性较高;该实现方案是最佳的选择。
三、如何防止用户作弊?
前面两个核心问题解决之后,如何防止用户作弊就非常简单了!
1. 超时未作答
即用户在当前题目中因为时间超时没有作答,但在下一道题目中提交了答案,如何处理呢?在用户提交下一题答案时,客户端会把上上题的 token 带上,【答题服务】很容易判断出 “跳题行为”,此时按答错处理即可!
2. 答错时继续作答
即用户已经答错一道题目,但该客户端用户还在继续作答,如何处理呢?在用户提交下一题时,客户端还是会把 token 带上,【答题服务】通过解密 token 很容易判断出上题已答错,所以本题仍按答错处理即可!
3. 多次提交不同答案
即用户针对每一道题目,都分别提交所有答案(每一道题目都是选择题,有A、B、C选项,客户端针对每一道题目都提交了三个答案),如何处理呢?为了降低处理的复杂度,【答题服务】在客户端请求时不处理这一种情况,在事后由【后台服务】来异步化处理即可(一场直播有 12 道题目,对于有超过 12 次答案提交的用户,直接列入黑名单即可)!
四、怎样快速统计数据?
针对每一道题目,当所有客户端提交作答后(也就是10秒作答时间之后),主持人会在直播中将正确答案进行公布,同时也会公布该题目的作答情况,即有多少人选择了A,多少人选择了B,多少人选择了C,多少人答对,多少人淘汰等。【答题服务】需要对这些数据进行快速统计,怎样落地实现呢?见下图。

首先,【答题服务】通过本地内存数据快速判断用户作答后,会在本地内存进行累加统计;然后开启一个独立的线程定时和定量地将累加统计数据写入到 Redis 中;【定时】是每隔 100ms 将内存数据写入到 Redis,【定量】是超过 100时将内存数据写入到 Redis;最后,【题目服务】直接从 Redis 中读取统计数据经由【Entry】下发到客户端。
题目作答的统计数据并非精准数据,所以即使【答题服务】节点挂掉后,损失的最多是 100ms 或 100 的累加数据,不会对结果造成严重影响。
最后,总结文中关键:
【直播答题系统】的四个核心问题及解决方案:
1. 判题逻辑如何实现? 采用答题服务实现方案;
2. 淘汰用户如何识别? 采用客户端存储实现方案;
3. 如何防止用户作弊? 对于【超时未作答】和【答错时继续作答】,通过解析 token 即可轻松识别(对于没有携带 token 和 token无法解密的,按答错处理即可);对于【多次提交不同答案】,事后异步处理即可;
4.怎样快速统计数据?【答题服务】先在本地内存累加,然后定时和定量写入到 Redis 即可。
对于【直播问答系统】整体架构设计,始终围绕着 【解耦】进行;对于核心问题解决,始终围绕着【减压】进行。
1067

被折叠的 条评论
为什么被折叠?



