1. 项目一:大额助力券

1.1 项目介绍

1.1.1 项目简介

  • 项目背景:在公司已有推荐有奖、天天领现金、社群等玩法的基础上,去年又新开了一个大额助力券的玩法,通过裂变的方式实现低成本的指数级用户增长。
  • 大额裂变券是一个社交裂变玩法。用户在页面看到活动后,可以发起分享给客态用户,客态用户可帮他助力,达到助力人数条件后成团,即可领取权益。用户还可以看到历史活动参与记录。

1.1.2 业务流程

1.1.3 项目架构图

1.1.4 一个请求的详细流程图

  • 如上图所示,一次请求的过程如下

    • 用户在浏览器中输入域名,经过ADNS域名解析得到一个VIP(张北Aserver的base域名),然后连接发起请求;
    • LVS负载均衡集群收到请求,通过FullNAT转发给对应的接入层集群AServer;
    • AServer集群收到请求会经过一定的逻辑处理(HTTPS相关服务等),从Http请求头的host获取请求的域名,根据域名找到对应的VIP server key,然后请求VIPServer获取业务主机IP,最后将请求转发给某台应用机器(MTOP去中心化)获取响应。
  • 名词解释:

    • ADNS:自研高性能DNS,提供了线路智能解析、机房自动/手动灾备切换、分机房流量调度等功能。会先将业务域名CName至gds域名,再为对应的gds域名分配对应的IP地址。
    • AServer:基于Nginx开发的统一网关接入,提供验签、防刷、反外挂等辅助功能,解决了实现https访问(证书申请与更换),证书泄露(私钥存在单独的安全服务器上),应用安全(接入了安全平台增加安全系数)等问题。
    • VIPServer:基于七层网络的负载均衡中间件,通过集中式的配置提供网络访问的路由信息、映射策略、健康监测等能力。还有实时健康状态检查、域名自动扩缩容、丰富的负载策略、机房快速切流、环境隔离容灾等特性。
    • MTOP:一个API网关平台,统一调用标准和SDK,发布后各个BU的App端和H5无缝调用;封装公共组件,对会话处理、安全校验、参数映射等通用性功能进行封装;完善运维体系,提供调用报表、监控告警、容灾等功能。

1.1.5 数据库设计

  • 活动表,券表,库存配置表,库存消耗表
  • 展示最近一年的数据,平均每天500万数据,一个月1.5亿数据,一年18亿数据,分128张表,每张表平均140万数据

1.1.6 机器配置

  • 应用服务器:11台,4核8G 硬盘60G
  • 限流配置:29台机器,单台机器抗500QPS,集群QPS 15000
  • MySQL:一主(leader)两从(follower、logger),8核16G 硬盘480G
  • Redis:4G集群(2分片),单分片2G,两主两从,
  • RocketMQ:一主多从

1.2 项目难点/亮点

1.2.1 分库分表后的多维度查询

0.分片键的选择

  • 因为预估数据量会比较大,所以MySQL采用了分库分表的方案,查询的时需要带上sharding key,但业务场景既要通过主态用户id查询主态助力记录,还要通过客态用户id查询客态助力记录,所以这就涉及到分片键的选择,选主态id或客态id都不合适。
  • 针对与这种多维度的查询有两种解决方案:映射法和冗余法,映射法需要额外增加一张表或使用缓存来记录主态id与客态id的对应关系,方案复杂,代价较大;而冗余法只需要额外增加一条记录即可,数据库里存两条,一条主态视角的助力记录,另一条是客态视角的助力记录。

①手动插入/回滚

  • 因为一次助力需要插入主客态两条助力记录,所以要保证原子性,项目初期,着急上线,时间紧,最早的方案是,如果插入或更新失败,手动代码里回滚,通过BCP的核对发现不少数据不一致的问题,比如说:主客态记录条数或助力状态对不上。原因:乐观锁

②分布式事务TXC

  • 因为MySQL是分库分表,所以原始的MySQL单机事务是不能用的,所以就要使用分布式事务,集团用的比较广泛的是TXC,其实对应的开源版本就是seata,我们选择的是AT模式,对业务入侵小,需要给每个库都要新建一张seata_undo_log表。
  • 项目初期数据量小(10万级),使用TXC还算平稳,但在全国放量后,数据量达到百万级后,在业务高峰期会出现小概率的TXC分布式事务回滚,但回滚时间3s左右,这是业务不能接受的。
  • TXC回滚原因:SQL执行异常或代码中抛异常,可能是由于行锁、间隙锁、buffer pool 刷脏页等引起。回滚导致性能降低100%左右。

③使用MQ保证最终一致性

  • 因为我们的业务场景是助力后首先展示的主态的助力记录,客态的助力记录查询场景少,页面入口深,客态助力后最快也需要3s左右才能进入客态助力列表,所以在保证用户体验的情况下,考虑使用最终一致性来代替强一致性。
  • 具体是通过MetaQ的事务消息来保证最终一致性,且保存主客态两份数据的业务场景适合最终一致性。首先插入主态视角的助力记录,如果主态视角的助力记录插入成功,则发送mq消息,异步落库客态视角的助力记录,否则返回助力失败,平均延迟300ms左右,上线后至今平稳运行。

1.2.2 平稳应对高并发流量

  • 前期通过压测摸底机器性能,11台机器抗1.32wQPS=1200 * 11,通过sentinel 限流来应对突发流量,保证系统的平稳运行。因为每天定时秒杀券活动QPS高峰是平时的10倍。
  • 不断优化接口,以提高QPS,将核心接口的耗时稳定在,平均RT < 80ms,TP99 < 200ms,优化手段:线程池调优、业务逻辑优化、外系统接口熔断、流程编排并发处理等。
  • 活动记录页接口平均RT降低了73%,TP99降低了81%,优化手段:线程池调优、批量接口代替单个接口,一次批量查库代替多次查库,业务逻辑优化(只展示最近3个月的记录,最多展示5个头像等),TPP接口增加熔断。
  • 潜在的缓存穿透、雪崩、热点key等问题,通过缓存库存null值来解决缓存穿透,设置随机过期时间和Redis的高可用来解决缓存雪崩,通过热点key缓存多份来解决热点key。

1.2.3 应用设计模式解耦复杂的业务逻辑

  • 简单工厂+策略:针对于同一个接口不同业务有不同的业务处理逻辑,使用借助spring的IOC实现简单工厂来创建具体的策略对象,然后根据不同业务来选择不同的策略进行实现。遵循了开闭原则,各个业务模块之间的迭代互不影响。避免使用多重条件转移语句。
  • 职责链模式:对于助力规则的校验,不同的业务可能有多种规则,使用责任链模式来校验每个规则,并做到了提供多种规则,运营可配置化,极大的降低了开发成本。降低耦合度:每个类只需要处理自己该处理的工作,不该处理的传递给下一个对象完成,明确各类的责任范围,符合单一职责原则。可根据需要自由组合工作流程。符合开闭原则。各个规则之间的修改也互不影响。
    便于增加新的请求处理类,而不需要修改代码,符合开闭原则。
  • 适配器模式:对于用户信息、发券、AB实验等第三方系统接口,通过适配器模式中的组合方式来实现实现ACL防腐层的建设,尽量将第三方系统接口的影响范围降到最小,收敛到只有一处调用点,参数和返回值统一处理为自己业务方容易识别的数据。增加了透明性和复用性。
  • 门面/外观模式:对于比较细节的功能,通过门面模式进行组装处理,方便调用,提高灵活性,屏蔽了子系统组件,减少了其它调用点处理的对象数目。比如:获取店铺信息功能,包含店铺名称logo地址、店铺链接、店铺评分、店铺标签、店铺配送信息等多个子接口,进行聚合封装,提高灵活性:对客户屏蔽了子系统组件,减少了客户处理的对象数目。低耦合:子系统变化不会影响客户端,客户端只需要调用外观类即可。
  • 模板方法模式:对于消费者的订阅过程,通过模板模式实现,抽出公共的启动消费者、消息体解析等处理逻辑,将topic、tag、consumerId等变化的部分定义抽象方法,由子类实现。符合开闭原则:封装不变部分,扩展可变部分。将相同部分的代码放在抽象的父类中,便于维护,减少重复代码,提高开发效率。实现了反向控制:行为由父类控制,子类实现。

1.2.4 DDD架构(亮点)

  • 从大额券到社交裂变玩法平台的建设,针对业务的复杂性,通过DDD架构支撑社交裂变玩法平台的搭建。
  • DDD实践,从战略设计到战术设计,不断优化,先是战略设计,从划分核心子域,支撑子域、通用子域开始,构建各个子域之间的上下文映射关系,然后是战术设计,通过构建充血模型的实体对象、值对象,聚合根,资源库,再到领域服务,完成整个领域驱动设计。
  • 使用DDD的收益点:①系统演进更快:因为DDD是业务架构映射到系统架构上,在响应业务变化调整业务架构时,也随之变化系统架构。所以在业务迭代的速度方面比传统的MVC架构要快30%左右。②方便测试和写单测,各个领域解耦相互独立。

1.3 待优化的点

1.3.1 分布式锁续期

  • 利用Redisson的看门狗续期

1.3.2 DAG优化并发流程

  • 问题:先并发调用AC任务,然后B任务依赖A任务的结果,任务ABC分别耗时10ms、200ms、300ms,三个任务耗时=max(10, 200) + 300 = 500ms
  • 优化:使用DAG进行优化,并发执行A → B任务和C任务,这样三个任务耗时=max(10 + 200, 300)=300ms

1.3.3 使用Lindrom只存一条助力关系

  • 使用Lindrom海量数据数据库,选择几列作为主键后,还可以建立索引,将主态用户id放入主键中,客态id建立索引,每次只需要插入或更新一条数据,就可以满足主/客态视角查询助力关系列表的场景了。

1.3.4 使用TXC进行库存扣减

  • 用户发券后的库存扣减,使用TXC分布式事务来保证一致性,而手动回滚易出错。

2. 项目二:抢红包

2.1 项目介绍

2.1.1 业务流程图

2.1.2 项目架构图

2.1.3 一个请求的详细流程图

  • 一个用户从发起请求到接收到响应。中间经过哪些服务,每个服务做什么事情的流程图。

    • LVS分流:抗负载能力强,工作在网络4层(传输层)仅做请求分发之用,没有流量,lvs+keepalived双机设置为不抢占模式,避免单点故障。
    • Nginx代理:工作在7层上,对网络依赖较小,工作在网络7层,配置简单,高性能、轻量级、内存消耗少,强大的负载均衡能力。当服务器规模庞大时,nginx 的网络带宽就成了瓶颈,所以使用LVS。
    • Ribbon客户端负载均衡:在调用微服务接口时,在注册中心上获取服务列表之后缓存到JVM本地,从而本地实现RPC远程。项目中通过@LoadBalanced注解用 Ribbon 为 RestTemplate 提供负载均衡的服务。 实现原理:LoadBalancerAutoConfiguration 中,注入了所有加了 @LoadBalanced 注解的 restTemplate的Bean 实例,将 restTemplate的Bean 实例设置到 RestTemplateCustomizer 中,然后给 restTemplate 设置 LoadBalancerInterceptor 拦截器。这样 restTemplate 在调用的时候会进入 LoadBalancerInterceptor 拦截器,拦截器中的 createRequest 中通过 ServiceRequestWrapper 来执行替换 URI 的逻辑,ServiceRequestWrapper 中将 URI 的获取交给 LoadBalancerClient#reconstructURI 方法。
    • 负载均衡策略:轮询(默认),权重,ip_hash,响应时间(RT短的优先分配),

2.1.4 数据库设计

  • 机器配置:①12台8c8g应用服务器;Tomcat优化: 默认配置(200线程NIO方式)②中间件:Redis集群扩容(10G)、MQ、MySQL高可用;③Nginx减压:静态文件推CDN
  • Tomcat最佳线程数量=((线程io时间+线程cpu时间)/线程cpu时间) * cpu数量。经验值:1核2G线程数200,4核8G线程数800

2.2 项目难点

2.2.1 压测

  • 压测范围

    • 超长分布式事务
    • 某个接口异常引起整个服务雪崩
    • 集群中某个节点重启或者机器重启,调用方反应明显
  • 压测发现的问题:
    • 一些情况下部署的集群和服务注册中心机器数量可能不一致,即服务节点被暴力干掉后,服务注册中心不能主动发现和踢出
    • 每个集群都存在负载不均的现象,个别机器可能 CPU 利用率会偏高。(和负载均衡策略有关)。单节点 CPU 负载较高时,负载均衡不会将流量路由到其它节点,即使这部分请求性能远差于其它节点,甚至出现很多超时(和负载均衡、熔断的实现机制有关)
    • kong网关扛不住、带宽不够

2.2.2 业务相关

  • 红包出现15s倒计时:①服务端(通过IM)下发消息:使用线程池延迟定时任务来替代MQ延迟队列;②前端主动轮询
  • 保证红包金额均匀分布:生成红包使用二倍均值算法,100元分为10个,保证10个红包的金额都在1-20元之间
  • 调整业务逻辑,将一些判断条件放在客户端,减轻服务端压力

2.2.3 解决缓存穿透、击穿、雪崩问题

  • 避免缓存雪崩:缓存key的过期时间是随机的
  • 避免缓存穿透:缓存NULL
  • 解决热点key/缓存击穿:热点数据、实时性要求不高(1s内)的数据使用多级缓存(本地缓存AtomicReference+Redis)
  • 数据提前预热:不变的信息(抢答的答案)放在本地缓存ConcurrentHashMap,常用数据放在redis,减少查库
  • 红包历史记录接口防止缓存穿透和击穿:先查缓存,如果缓存不存在,加分布式锁请求排队等待

2.3 项目亮点

2.3.1 设计

  • 前端

    • 活动/推广相对独立的项目建议使用新域名,利于安全、防护、降级等策略的实施。
    • 静态资源:页面、js、css、图片等资源使用CDN缓存。
    • API聚合:一个页面一个API,一个按钮一个API,尽量使用推的方式,如果需要轮询,一个页面最多一个轮训。
    • 重复点击:所有点击按钮需要防重复点击。
  • 服务端

    • 尽量使用新服务,中间件尽量使用新集群,利于资源隔离,避免影响正常业务
    • 支持降级:API设计划分重要程度(可降级、不可降级)。兼容降级策略(降级API返回的http code码为429)给用户合理的提示。
    • 禁止事务:不要使用数据库事务,优先使用分布式锁。
    • 提高接口响应时间,API越快,单机吞吐越高,支持并发量越大。

2.3.2 支持高并发

  • 缓存优先:能用缓存就用缓存,避免直接依赖数据库,避免依赖外系统,避免依赖第三方服务,红包放在使用Redis,支持上万的并发
  • 提高并发:QPS = 并发数/RT,QPS与并发数成正比,与RT成反比。并发数通常由服务器的处理能力和服务器数量决定,RT则主要是由服务端接口处理性能决定了。所以,要提高QPS,可以通过提升机器配置,增加机器数量以及优化接口性能的方式来处理。
  • 削峰:(1) 前端削峰:例如抽奖报名的按钮,前端js控制,5s内随机显示,将请求散列到5s内。(2) 服务端削峰:①对接口使用Sentinel熔断限流,返回友好提示;②使用MQ异步处理更新红包明细,生成小红包等③使用RateLimiter控制MySQL写入并发量(负载均衡根据ip路由)

2.3.3 限流算法

  • 令牌桶算法:以一个恒定的速度往桶里放入令牌,如果请求需要被处理,则先从桶里获取一个令牌,当桶里没有令牌可取时,则拒绝服务。

    • 通过 SmoothBursty (稳定)类和 SmoothWarmingUp(渐进/平滑) 类实现。平滑即一开始的速率较慢,慢慢提高到期望值。
    • 通过synchronized的私有锁保证速率的修改是线程安全的,如果当前的时间比预计下次可发放令牌的时间大,则更新nextFreeTicketMicros为当前时间。利用延迟计算的方法,把桶里令牌数量的计算放在下一个请求中计算。
    • 符合单机QPS阈值:每秒钟向令牌桶钟投入maxClusterQpsThreshold/serviceNodeNum个令牌
    • 不仅能够限制数据的平均传输速率,还适合某种程度的突发传输
  • 漏桶算法:以恒定的速率从桶里取请求。把请求比作是水,水来了都先放进桶里(5s内有效),并以限定的速度出水,当水来得过猛而出水不够快时就会导致水直接溢出拒绝服务(过期重新从redis中取值)。
  • 滑动窗口:优点是解决流量突发和临界值问题,配置可调整。在 Sentinel 里面,所有的资源都对应一个资源名称以及一个 Entry。Entry 可以通过对主流框架的适配自动创建,也可以通过注解的方式或调用 API 显式创建;每一个 Entry 创建的时候,同时也会创建一系列功能插槽(slot chain)。
    • FlowSlot 插槽用于根据预设的限流规则以及前面 slot 统计的状态(每1ms统计一次),来进行流量控制;依赖于StatisticSlot 插槽记录、统计不同纬度的 runtime 指标监控信息;集群限流 通过 TokenService实现
    • NodeSelectorSlot 负责收集资源的路径,并将这些资源的调用路径,以树状结构存储起来,用于根据调用路径来限流降级;
    • QPS限流:根据当前Node对应的ClusterNode(默认)的统计信息进行限流。获取节点的当前qps计数;判断获取新的计数后是否超过阈值超过阈值单返回false,表示被限流,后面会抛出FlowException。否则返回true,不被限流。
    • 熔断规则:响应时间RT、异常比例、异常数。通常是调用方做熔断,比如:rpc请求响应时间超过3s就熔断,这时候服务端可能已经挂了。
    • DegradeSlot 则通过统计信息以及预设的规则,来做熔断降级;通过限制资源并发线程的数量,没有线程切换的损耗,也不需要预先分配线程池的大小。
    • 降级:服务端自身的保护,例如:服务端资源不够用了。

2.3.4 通过该项目发现了框架的一些待优化点

  • Redis框架:可以开放eval指令,支持执行LUA脚本,方便在高并发下进行原子性操作。开放redis管道操作API,减少频繁读写redis时IO带来的开销,以获得更好的性能;
  • MySQL框架:findById方法需要优化下缓存穿透和缓存击穿的问题;save方法可以加入限流逻辑,减少对写库造成的压力
  • 延时调度器:可以提供一个进行随机延时的异步任务的延迟调度器。

2.3.5 红包分配算法符合正态分布

  • 与其它分布的区别:正态分布是连续分布,泊松分布,二项分布都是离散分布; 二项分布的极限分布是泊松分布、泊松分布的极限分布是正态分布。正态分布符合中心极限定理:在特定条件下,大量统计独立的随机变量的平均值的分布趋于正态分布。
  • 正态(高斯)分布定义:若随机变量X服从一个数学期望为 μ\muμ 、方差为 σ2\sigma ^ 2σ2 的高斯分布,记为N(μ, σ2\sigma ^ 2σ2 )。其中期望值(均数) μ\muμ 决定了位置,其标准差 σ\sigmaσ 决定了分布的幅度( σ\sigmaσ 越小曲线越尖峭)。N(0, 1)表示标准正态分布。可以利用正态分布近似估算二项分布和泊松分布。正态分布函数密度曲线公式如下:
    f(x)=1σ2πe−12(x−μσ)2f\relax{(x)} = \frac 1 {\sigma\sqrt{2\pi}} e ^ {- \frac 1 2 \left( \frac {x-\mu} \sigma\ \right)^2 } f(x)=σ2π​1​e−21​(σx−μ​ )2
  • 代码实现:java8线程安全的Random.nextGaussian()使用了极坐标法(Box–Muller的一种形式),该方法来源:单位圆 -> 均匀分布 -> 指数分布 -> 自由度为2的卡方分布 -> 反推出2个随机变量的表达式,避免了sin⁡,cos⁡\sin,\cossin,cos 的计算,具体理论(nextGaussian方法源码剖析)
    ①随机变量 U,V 服从(-1,1) 的均匀分布
    ②计算 S=U2+V2S=U^2+V^2S=U2+V2
    ③如果 S≥1S \ge 1S≥1, 接着计算, 否则转④
    ④X=U−2ln⁡SS,Y=V−2ln⁡SSX=U\sqrt{\frac{-2\ln S}{S}},Y=V\sqrt{\frac{-2\ln S}{S}}X=US−2lnS​​,Y=VS−2lnS​​, X,Y 独立,并且都符合高斯正态分布
  • 正态分布的特点:符合68-95-99.7法则,即68.3%的面积在平均数左右的一个标准差 σ\sigmaσ 范围内;95.4%两个 σ\sigmaσ 范围内,99.7%三个 σ\sigmaσ 范围内;99.9%四个 σ\sigmaσ 范围内。
  • 使用正态分布的缺点:无法近似估算符合几何分布的问题,无法精确解决离散数据概率。
  • 其它算法:
    • Box–Muller(博克斯·马勒)变换:本质用了逆变换法,以两组独立的随机数U和V,这两组数在(0,1]上均匀分布,用U和V生成两组独立的标准正态分布随机变量X和Y ,公式和java代码实现如下:
      Y=−2ln⁡Usin⁡(2πV)Y = \sqrt{- 2 \ln U} \, \sin(2 \pi V)Y=−2lnU​sin(2πV)
      X=−2ln⁡Ucos⁡(2πV)X = \sqrt{- 2 \ln U} \, \cos(2 \pi V)X=−2lnU​cos(2πV)
    • ziggurat算法:比Box–Muller更快,利用拒绝采样实现。
    • 线性同余算法:生成一个均匀分布的伪随机数,例如:java.util.Random.next()方法,有规则的随机,使用的是48-bit的种子,然后调用一个linear congruential formula线性同余方程,特点:在给定种(seed)的区间内随机生成数字。相同种子数的Random对象,相同次数生成的随机数字是完全相同的。
  • 生成红包(1-20元)优化前后的散点对比图

2.4 设计秒杀系统要考虑哪些点?

  1. 前端限流:与产品沟通,前端限流方式是否影响用户体验(如答题、拼图等)
  2. 预估用户量:比如只有40%的用户能抢到,那么直接让50%的用户立即秒杀失败,不进入后续服务
  3. 数据预热:秒杀都是瞬时操作,不要等流量来了再加载数据。可以提前对数据进行预热,比如加载到缓存等。
  4. 缓存:包括CDN缓存和数据缓存。保证缓存系统的高可用,数据随后落地。
  5. 解决超卖:引入MQ,串行化操作库存,达到阈值后不再消费,并关闭购买功能。或者直接操作缓存。下单即锁定库存还是支付才锁定库存。可使用mysql的乐观锁(version)解决库存超卖问题。
  6. 流量削峰:通过引入MQ,将耗时业务进行削峰,平稳处理用户需求。
  7. 熔断限流:熔断,优先保证主要业务的进行。限流,识别异常流量,进行封锁;同时,允许部分请求失败。
  8. 弹性扩容:在判断系统负载达到极限时,可以通过增加服务器的途径抵抗峰值。需要打通运维环境,能够快速扩容。临时机器、带宽申请,环境隔离,不要影响主体业务。

3. 项目三: 直播

3.1 项目架构图

  • 项目架构图

  • 一个请求的详细流程图

  • 数据库设计

3.2 项目难点

3.2.1 设计难点

  • 1、音视频处理及传输:涉及音视频编码、实时美颜、视频推流、CDN加速分发、终端适配和播放,流量统计等诸多技术点,而且当时我们公司没有专门做音视频方面的专家。

    • 技术团队不具备这方面的能力,所以当时调研了各种云厂商的付费解决方案,最终采用了腾讯云的直播解决方案。医生端:APP端集成腾讯云的SDK可进行视频录制和推流。用户端:服务端推送拉流地址后即可观看直播。
  • 2、高并发请求:结合网站用户的活跃度和活动推广,预计会有100W用户同时在线,1秒内单个用户可触屏发起4-5次抢红包请求,并发最大可达到500W QPS。
  • 3、高带宽压力:按照标清视频的标准,观看直播的码流至少为1Mbps,如果100W用户在线,光视频流的出口带宽能达到976.56G bps。1条弹幕可达到130字节,1秒要滚屏20条弹幕,如果需要同时推送给100W用户,弹幕的出口带宽也将达到19.37G bps。
  • 4、高计算压力:分发红包,运营在后台发多个红包,然后拆分为多个小红包,然后百万用户同时抢。
    • 提前计算好小红包的金额,存入redis中,减轻服务端压力,前端拦截部分请求。
  • 5、资金流的正确性和安全性:单个用户最多抢3个红包如何不多领?财务上如何保证答题奖金、红包奖励不出现1分钱的误差?
  • 6、低延迟性要求:直播场景下如何整合视频流和业务数据流,做到声音、主播画面和题目同步,以保证用户体验?
  • 7、对公司其它业务的低干扰:好大夫峰会作为公司的一个运营活动,核心目标是导流,它依托好大夫原有的用户体系,运营系统,大数据系统,提现通道等,如何做到对好大夫现有系统的低干扰?

3.2.2 实时统计在线人数

  • 原方案:①服务端缓存一个在线人数的redis, 前端通过调用进入直播间、离开直播间接口对在线人数的redis 进行+1、-1操作。②服务端起一个定时任务,1分钟执行一次,从redis 中将缓存的在线人数入库,入库之后会将redis 缓存的在线人数清零。③前端每一秒调用一次服务端获取在线人数接口,服务端在线人数接口是从redis 里取在线人数缓存+从数据库中取的在线人数(走的是数据库实体缓存)返回给前端。
  • 存在的问题: 线上偶尔出现突然在线人数几十变成0的情况(最少应该也有一个人才对)
  • 查看代码猜测问题:①先清除缓存再存库,如果数据入库失败,则在线人数统计数据丢失。②前端如果存在一次离开直播间多次调离开直播间接口的情况,也会导致数据为0.
  • 加日志定位问题:redis+1、-1操作日志中,增加变更前值和userId 的日志输出,发现是删除缓存成功,但更新数据库失败
  • 解决方法:如果更新数据库失败,把数据重新set到缓存,保证数据不丢失

3.2.3 快速过滤直播弹幕敏感词

  • 业务场景:需要快速识别大量的直播弹幕是否包含敏感词,敏感词库比较大,需要实时判断。
  • 解决方案:使用DFA(Deterministic Finite Automaton),即确定有穷自动机算法。该算法原理:基于状态转移来检索敏感词,把敏感词组成树形结构,就可以减少检索次数,只需要扫描一次待检测文本,就能对所有敏感词进行检测。
  • 出现的问题:敏感词中包含空格、#等无效字符,还有英文大小写的问题,分别通过过滤无效字符、统一转小写来判断。

3.3 项目亮点

  • 保证了用户观看视频低延迟,支持几十万级的并发用户同时在线观看直播

4. 项目四: 地城之歌

4.1 项目架构图

  • H5地城之歌游戏架构图

  • WebService:负责维护服务器列表,记录服务器状态信息、玩家账号信息(渠道第三方用户ID是否在游戏内已经创建账号,该ID登陆过的服务器信息,该ID使用过的特殊礼品码信息)等。

  • Logic Server:逻辑服务器,负责游戏内所有系统功能活动的实现。通过WebSocket协议与前端保持通讯关系。数据库持久化采用Mybatis3,通讯框架采用Netty4。

  • Post Server:邮差,内部中转服务器。各个不同职责的服务器跟PostServer建立TCP/IP连接,所有服务器之间的请求,都通过Post Server 转发,不同职责的服务器之间没有建立直连。

  • State Server:状态服务器,负责跨逻辑服的功能实现。

  • Statistics Server:日志记录服务器。通过log4j记录玩家的操作行为,并追加到log文件中去。

  • GM Web System:GM后台系统。前端采用AngularJS框架,后端采用Jersey框架。

4.2 项目难点

  • 缓存与数据库的双写不一致问题,解决方法:

  • 先更新数据库再更新缓存

    • 缺点1:浪费资源,消耗CPU资源,写多读少的情况会有冷数据。
    • 缺点2:数据脏读:A在B之前更新数据库,由于网络延迟,却在B之后更新缓存。
    • 适合场景:写少读多,如博客、基本信息等
  • 先更新数据库,再删除缓存

    • 缺点:若由于网络原因删除缓存失败,那么缓存中就是旧数据。(可设置key的有效时间)
    • 解法方法:可将更新失败的缓存放入MQ中,或通过订阅binlog(canal)来更新缓存,但有延迟。
    • 适合场景:对于实时性要求不高,允许有一定的延迟产生。
  • 先删除缓存,再更新数据库

    • 优点:若删除缓存失败,就不更新数据库,如果删除缓存成功,而更新数据库失败,那查询的时候只是从数据库里查了旧的数据而已,能保持数据库与缓存的一致性。
    • 缺点1:若删除完缓存时有请求会直接查询数据库。
    • 解决方法:将删除缓存时的发生请求放入一个队列中,一个队列对应一个工作线程。优化点:若有相同的更新缓存操作,可用一个while(true)循环去查询缓存(设置超时时间),等待前面的操作完成。
    • 可能会出现的问题:读请求长时间阻塞:设置超时时间,在超时时间内返回。高并发的读请求:根据压测情况扩容。多服务实例的请求路由:可通过nginx服务器路由到相同的服务实例。热点商品导致请求倾斜:影响不会太大。
    • 总结:延时双删缓存 + 订阅binlog更新缓存。

4.3 项目亮点

  • 使用Netty作为底层的通信框架,可支持十万级的长连接,性能高。
  • 将登陆、充值、匹配、日志等模块分开部署,互不影响,类似于一个简单的微服务框架。

参考:

  1. 当前疫情下火爆的直播应用,你了解背后的技术架构吗?
  2. LVS 与 Nginx 区别
  3. 客户端负载均衡-Ribbon
  4. 漫谈正态分布的生成
  5. Java中的random函数是如何实现的
  6. RateLimiter源码剖析

【面试题】15.项目相关相关推荐

  1. [剑指offer]面试题15:链表中倒数第k个结点

    面试题15:链表中倒数第k个结点 题目:输入一个链表,输出该链表中倒数第 k 个结点.为了符合大多数人的习惯,本题从1 开始计数,即链表的尾结点是倒数第1 个结点.例如一个链表有6个结点,从头结点开始 ...

  2. 软考新思维--2017年上半年信息系统项目管理师上午试题分析与答案(试题1-5题)

    2017年上半年信息系统项目管理师上午试题分析与答案(试题1-5题) 1.信息系统是由计算机硬件.网络通讯设备.计算机软件,以及()组成的人机一体化系统. A.信息资源.信息用户和规章制度 B.信息资 ...

  3. vuecli 编译后部署_基于vue-cli 打包时抽离项目相关配置文件详解

    前言:当使用vue-cli进行开发时时常需要动态配置一些设置,比如接口的请求地址(axios.defaults.baseURL),这些设置可能需要在项目编译后再进行设置的,所以在vue-cli里我们需 ...

  4. PMP-【第13章 项目相关方管理】-2021-2-17(272页-291页)

    1.项目相关方的基本概念 2.项目相关方管理的注意点 3.主要项目相关方 4.相关方凸显模型 5.疑难问题

  5. 【项目管理】认识项目相关方(干系人)管理

    在项目管理中,相关方的管理的重要性尤为重要.合格的项目经理往往能够很好把握管理项目相关方的项目诉求期望(范围,进度,质量,风险,沟通等),做到项目利益最大化,通过一系列的工具和方法,高效捕获相关方的需 ...

  6. 动态链接库、静态库区别与VS2005项目相关设置

    出处:http://blog.csdn.net/Ocean2006/archive/2010/02/05/5289908.aspx 一.动态链接库.静态库区别 二.VS2005项目相关设置 三.#pr ...

  7. Java面试题15牛客 以下关于Integer与int的区别错误的是

    Java面试题15牛客 以下关于Integer与int的区别错误的是 A int是java提供的8种原始数据类型之一 B Integer是java为int提供的封装类 C int的默认值为0 D In ...

  8. Day5 - 前端高频面试题之计算机网络相关

    传送门>>> 上一篇: Day4 - 前端高频面试题之浏览器相关 1.请介绍一下HTTP和HTTPS的区别? HTTPS是在HTTP的基础上加入了SSL协议,SSL依靠证书来验证服务 ...

  9. IntelliJ IDEA 项目相关的几个重要概念介绍

    IntelliJ IDEA 项目相关的几个重要概念介绍 发布于 2015-09-23 23:02:48 | 11 次阅读 | 评论: 0 | 来源: 网络整理 本篇内容为大家提供的是IntelliJ ...

  10. PMP学习笔记 第13章 项目相关方管理

    第13章 项目相关方管理 项目相关方管理的核心概念(P504) 相关方:会受项目的积极或消极影响,或者能对项目施加积极或消极的影响的任何人. 在老版本里面,相关方称为:干系人 强调结构化方法对识别所有 ...

最新文章

  1. php exec和query,关于Go SQL中的Query、Exec和Prepare使用对比(附网络抓包)
  2. 2.6 处理数据不匹配问题-深度学习第三课《结构化机器学习项目》-Stanford吴恩达教授
  3. C/C++入门易错点及常用小技巧
  4. 如何打开java_怎样运行java
  5. java 课后习题 找零钱
  6. 第十四篇 元类编程(二)
  7. sqlserver 中怎样查看一个数据库中表的关系
  8. html 隐藏广告代码大全,JS广告代码_JS广告代码大全_js特效代码_js特效代码大全 - 懒人建站...
  9. python源码剖析_Python源码剖析-深度探索动态语言核心技术.陈儒.PDF版www.atcpu.com...
  10. linux系统编译Q,Linux下安装qBittorrent,开启24小时挂机BT下载
  11. python快速入门【四】-----各类函数创建
  12. 精通手游运维的架构体系
  13. 完美解决Failed to configure a DataSource: ‘url‘ attribute is not specified and no embedded datasource的问题
  14. Excel中如何进行快速单位换算
  15. http://www.115ps.com/all.html,Tynamo
  16. 马斯克身价接近450亿美元,超过马云仍低于马化腾
  17. Netty源码分析系列之常用解码器(下)——LengthFieldBasedFrameDecoder
  18. 杂谈Android身份证识别技术
  19. Java原子操作和CAS
  20. 自动灌溉c语言程序设计,基于单片机的自动灌溉系统开题报告.doc

热门文章

  1. python储物柜难题_转角那1㎡不做储物间?太浪费了!好好利用还解决收纳难题...
  2. 题解 UVA12304 【2D Geometry 110 in 1!】
  3. 高德离线数据api_高德地图Android API发布V1.0.4版本,增加离线地图功能
  4. 下厨房app竞品分析(产品和用户)
  5. 学校快递代领PHP源码_PHP校园帮忙领取快递平台
  6. 软件测试精华总结,入门到精通全流程(必看,知识点很全)
  7. 企业注销的债权债务如何处理
  8. C语言商品订购系统(跟购物系统有些差别)
  9. 基因、DNA、碱基、染色体之间的关系是什么?
  10. Vue3 Mixin的使用方法(全局,局部,setup内部使用)