大促流量激增,通过什么手段提升系统的高并发高可用性?
Posted Javaesandyou
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了大促流量激增,通过什么手段提升系统的高并发高可用性?相关的知识,希望对你有一定的参考价值。
在之前提到过,最近运营在前面搞了一个活动,比如抽奖,发放优惠券等方式,去吸引用户购买商品,但是卡券的有效时间只有2个小时,这样的话用户支付动作会比较集中,这也就对我们的支付系统有了更高的要求。所以,如何在流量激增的情况下保证支付服务的高并发和高可用性是对我们的要求。
经过一系列的压测、调优,达到了预期的QPS。结果不是最重要的,最重要的是期间的调优过程,所以将最近在压测过程中出现的问题以及调优的过程复盘并记录下来,以便日后查看。如果大家面临类似流量突增的情况会怎么调优呢?
网络架构图
上图为我们目前简易的一个网络架构以及组件应用图,我将按照网络流量传递方式进行总结。
SLB
首先进行配置升级的就是流量入口的位置,即SLB。按照预估流量,以前SLB的QPS为5W,这次大促活动,将核心模块SLB的QPS升级至10W。
nginx
接下来是负载均衡组件Nginx,因为本身Nginx的QPS就是万级别以上了,我们在压测过程中发现Nginx并不是瓶颈,所以就没有进行升配。
网络
因为我们的应用服务目前存在私有部署和阿里云部署,所以中间存在着网络专线,所以本次压测过程中重点关注了网络延迟,发现其中的网络延迟为毫秒级,可以接受,并未对网络带宽进行升级,不过这也是一个其中的一个风险点,如果在活动期间专线网络存在抖动的情况,可能会影响到核心服务的质量。
阿里云ECS
下面就到达了具体的服务应用部分,因为我们的服务全部都是通过网关(SpringCloud Gateway)进行统一服务转发的,所以在经过压测和流量评估后将网关进行了机器扩容和配置升级,由原来的4C8G升级为8C16G。
对于核心业务交易模块的机器也进行了翻倍扩容,以此来增加系统的整体吞吐量,增加机器后集群整体TPS和QPS提升比较明显。
Tomcat
本次调优过程中,对Tomcat核心参数也进行了调优,主要包含最大连接数和最大线程数等,对于4C8G对机器来说,建议设置参数如下:
server:
tomcat:
accept-count: 1000
max-connections: 10000
max-threads: 800
min-spare-threads: 100
缓存
提高系统查询性能的利器就是使用缓存了,以此来提升系统的访问速度、增大系统的处理容量、提升性能和缓解数据库压力。对于一些不怎么经常变得数据,我们使用的Redis来作为缓存。不过使用Redis作为缓存也需要考虑其可能发生的问题,也就是缓存穿透、缓存击穿和缓存雪崩的问题。针对以上问题,行业上都有比较成熟的解决方案,下面我们依次解释,并记录下处理方案。
缓存穿透:请求去查询一条数据库中压根就不存在的数据,也就是缓存和数据库都查询不到这条数据,这样的话请求每次都会打到数据库上面去。
穿透产生问题:如果有黑客对你的系统进行攻击,拿一个不存在的id 去查询数据,会产生大量的请求到数据 库去查询。可能会导致你的数据库由于压力过大而宕掉。
穿透解决方案:1)缓存中没有存储这些空数据的key。去数据库中查询也没有, 那么我们就可以为这些key对应的值设置为null丢到缓存里面去。后面再出现查询这个key 的请求的时候,直接返回null ;
2)布隆过滤器是将所有可能存在的Key的Hash到一个足够大的bitmap中,在缓存之前再加一层布隆过滤器 ,在查询的时候先去布隆过滤器去查询key是否存在,如果不存在就直接返回,存在再走查缓存或者查DB。
缓存击穿:在高并发的系统中,大量的请求同时查询一个 key 时,此时这个key正好失效了,就会导致大量的请求都打到数据库上面去。
击穿产生问题:在高并发的系统中,大量的请求同时查询一个 key 时,此时这个key正好失效了,就会导致大量的请求都打到数据库上面去。
击穿解决方案:1)可以在第一个查询数据的请求上使用一个互斥锁来锁住它。其他的线程走到这一步拿不到锁就等着,等第一个线程查询到了数据,然后做缓存。后面的线程进来发现已经有缓存了,就直接走缓存。
2)超时预判:在value内部设置1个超时值t1, t1比实际的超时时间t2小,当读时发现t1已过期,先加锁延长t1并重新设置到cache, 同时通知查库,查库期间Cache返回旧值,有一个线程从数据库加载数据并设置到cache中. 此后Cache返回新值;
3)预热:先将热数据先加载到缓存系统, 请求直接查询. 后台线程异步同步/定时更新。
缓存雪崩:指当Redis宕机或给所有的缓存设置了同样的过期时间,当某一时刻,整个缓存的数据全部过期了,然后瞬间所有的请求都被抛向了数据库,数据库就崩掉了。
雪崩产生的问题:会造成某一时刻数据库请求量过大,压力剧增。
雪崩解决方案:1)宕机:对缓存集群实现高可用,如果是使用 Redis,可以使用主从+哨兵 ,Redis Cluster 来避免 Redis 全盘崩溃的情况;
2)缓存失效:为了避免这些热点的数据集中失效,那么我们在设置缓存过期时间的时候,我们让他们失效的时间错开。比如在一个基础的时间上加上或者减去一个范围内的随机值。
以上是对应用缓存提升查询接口QPS的方式以及问题处理的总结。
异步化
异步化主要包含两部分,第一部分是分布式系统之间的异步化,主要通过MQ来实现,我们这里使用的阿里云付费版RocketMQ(ONS),在经过压测和评估之后,首先将TPS升级由2W升级至5W,以提高系统的吞吐量。还有就是利用MQ的消峰、异步解耦特性,对于核心业务非必须同步返回结果的接口同步MQ异步优化,这样极大了提高了接口的响应性能。但是在使用MQ时也需要注意一些问题,比如:消息的事务问题、消息顺序问题、消息的防重和幂等一些列的问题。
异步化的第二部分是在单机JVM来进行,比如线程池、程序调用多方接口采用Future模式等。
JVM
这也是本次调优的一个关键点!在首次压测的过程中,压测性能老师反馈存在卡顿的现象,经过排查监控发现是新生代存在频繁的Yong GC且GC时间长达1.5S,这也是导致卡顿产生的原因,老系统的新生代设置的Eden和S1、S2的比例为1:1:1也非常不合理,调优后将比例调整为8:1:1,增加Eden的内存空间。同时Metespace的空间分配也过小,还有就是有些系统竟然没有设置Xms,导致JVM的可用内存非常小,不断发生频繁的Full GC,调优后JVM的可用内存才恢复正常,效果如下图:
JVM使用情况
针对4C8G的机器,在设置JVM参数的时候,建议设置为:
CMS配置参考:-Xms4608M -Xmx4608M -Xmn2048M -XX:PermSize=256M -XX:MaxPermSize=256M -XX:TargetSurvivorRatio=60 -XX:CMSInitiatingOccupancyFraction=75
G1配置参考:-Xms4608M -Xmx4608M -XX:+UseG1GC -XX:MaxGCPauseMillis=200 -XX:+UseStringDeduplication
系统日志
在压测的过程中,发现只要流量一上来,会产生大量的磁盘IO,这期间的接口响应也会变慢,经过分析是打印大量日志和日志中存在大JSON数据导致,于是升级日志打印级别同时将同步日志打印改为异步,系统提升性能效果明显。logback.xml修改如下:
<appender name="RollingFile_Async"
class="ch.qos.logback.classic.AsyncAppender" neverBlock="true">
<queueSize>8096</queueSize>
<neverBlock>true</neverBlock>
<appender-ref ref="RollingFileAppender"/>
</appender>
<root level="info">
<appender-ref ref="RollingFile_Async"/>
</root>
数据库
对于核心业务操作必然涉及到最终到落库操作,数据库优化也是本次调优的重点,主要从一下几个方面进行优化:
- 升级数据库配置(DRDS升级配置由32C128G升级至64C256G);
- RDS增加从库,数据读写分离,增加查询性能;
- 连接池最小连接数由5改成20,最大连接数由60改成200;
- 慢SQL优化;
- 分页查询。
ES
针对聚合搜索的业务,本次对ES也进行了调优,最主要对目的都是让FileSystem Cache能够缓存更多的数据,我们做了按照年份对数据做了冷热数据分离、数据预热等,具体细节我梳理一下,见下图:
ES优化细节
限流、降级
为了保证系统的高可用,系统必须采取一定的降级和限流手段,防止发生服务雪崩的情况。比如降级,针对一些不重要的展示信息,由于这些信息涉及到关联表查询等,影响系统的整体性能,所以在大促期间将这些不重要的信息进行降级处理。限流主要是针对找过预估的流量按照QPS进行限制。
好的,以上是针对本次大促活动进行的调优整理,希望可以帮助到大家,如果有其他更好的方式也欢迎留言讨论。
不断分享开发过程用到的技术和面试时经常被问到的问题,如果您也对IT技术比较感兴趣可以「关注」我,让我们共同学习,共同进步!
面向大规模流量活动的高可用架构实践
在高可用社区做过的分享:
https://mp.weixin.qq.com/s/NPsCkI-k0d73qadt-2yiNw
分享的主体内容:
分享的内容主要分为三个部分:
1.大流量活动的系统扩容评估方法
2.系统高可用架构设计实践
3.大规模流量活动的实践案例
大流量活动的系统扩容评估方法
大流量活动有多种形式,除了我们常见的电商大促(双11、618等),近几年还兴起了一种新形式——直播卖货。一个知名主播在比较短的时间里,引导粉丝大量下单,这是一种典型的大流量场景,对系统的稳定性发出较高挑战。 另外一种比较常见的活动形式是营销活动,例如春节红包、周年庆。
活动的流量来源,包括内部资源、外部付费购买的广告资源和第三方合作导流。公司在活动上投入了大量的资金,参与用户众多,如果活动中途出现问题,不仅对产品口碑的影响非常负面,而且会有金钱损失,还涉及到与第三方合作失败的问题。
举个例子,假设你的活动跟春晚合作,春晚到了某个环节让大家拿起手机现场扫码,这时系统挂了,扫码失败,这种情形就非常尴尬。遇到这种情况,第一个找你的可能不是你的领导,而是外部的第三方合作商,由此可见,关键时刻的系统高可用有多重要。
大流量活动面临的挑战主要分为三个部分。
第一个挑战是流量评估难,扩容的量级并不是张口就来,如果你跟运维说要扩充10倍容量,他一定会反问你为什么,你要拿出扩容的合理依据。
第二个挑战是架构扩容难,即使我们评估出了比较准确的流量峰值,又该怎么进行系统扩容呢?现代IT系统架构的复杂度越来越高,尤其是现在流行的微服务架构,它的节点数越来越多,服务之间的调用关系非常复杂,链路的梳理本身也是一大难点。
第三个挑战是怎么进行高可用实施,我们把流量评估和链路梳理扩容都做好了,活动在大流量到来的那天就没有问题了吗?当然不是,所以第三个问题就是怎么进行高可用实施。
想要解决上述挑战,绕不开业务和系统的双重复杂度问题。 微服务架构的复杂性,加之业务的复杂性,使得整个系统错综复杂、难以被人类理解。即使是公司的架构师、业务研发同学也很难讲清楚系统整个链路的每一个细节和服务之间的调用关系,因为此时的系统已经是一张庞大又相互交织的的网络了。
要解决流量评估的问题就得进行精细的链路梳理,梳理的第一步就是画架构简图,把每个架构大的模块画出来,再根据架构简图进行业务功能链路拆解。
部分链路图
链路图梳理完成后,我们就可以开始预估扩容量了,这里涉及到三个重要的指标:推广量、环节转化率、单UV的请求总数。
第一个指标推广量,一般情况下会以每秒广告的曝光量计算。将不同广告渠道的曝光量加起来就能得到预估的推广量级了。 还有一种方式是计算APP应用高峰每秒用户的登录数,这个数值约等于推广的每秒曝光量。 一般预测的每秒推广量都在系统现有容量的10倍以上,按这个推广量去扩容,系统基本没有问题,但会它会造成比较严重的资源浪费。
这时则需要用第二个参数来辅佐我们进行更细致的评估,它叫做环节转化率。以红包活动为例,用户看见红包活动,但不一定会点击进入页面,进入页面也不一定会领取红包,每一个环节都有环节转化率。 预估环节转化率一般有两种方法,一种是依据过往的经验数据进行估算;另一种就是提前演习,提前给部分用户推送活动,快速准确地收集到每一个环节转化率,有条件的公司一般会采用这种方式。
还有一个参数是我们需要特别注意的,就是单UV的请求总数,为什么要特别关注呢?
因为用户进入一个活动页面,他可能会查询个人信息、查看活动记录,通常不止一个请求动作,所以你在计算后端压力时不能只算用户进入活动的每秒峰值,还要算上其他请求动作。假设用户平均有4个请求动作,那你的流量就要乘以4,它有一个放大效应。
通过前面3个指标我们就能计算出后端会承受多少流量压力,进而得出扩容的预估值。最后把扩容容量设置为预估值的3倍,这个3倍并不是凭空臆想,而是从过往大量的活动扩容经验中总结得来的规律,活动当天的峰值通常是常规均值的3倍。
具体怎么计算咱们来举个例子,假设我们是100/S的曝光量,只有60%用户进入了领取页面,这其中又只有50%点击了领取按钮,那后端收到的请求是30/S,根据单UV的请求总数来说,整个后端最少要支撑120/S的压力,加上3倍原则那么后端最终的扩容也就是360/S。领取页面的流量峰值则为180/S,这时你带着数据去找运维扩容就有理有据了。
某活动容量评估得到结果是9.6W/S,根据三倍原则,整个业务链路要扩容到30W/S,但是,在实操扩容过程中会发现部分应用很难扩容。举个例子,像Web server这种很容易做平行扩容,只要机器足够就可以扩容上去,但有些接口日常也就2000-3000/S的QPS,要扩到10万QPS基本就是不可能的,也没有那么多的资源和精力。这就涉及到扩容的具体实践了,也是我们接下来要说的系统高可用架构设计的实践内容。
系统高可用架构设计实践
通常来说,我们系统高可用架构实践有三大扩容要素:全链路QPS、机器带宽、存储大小。
全链路QPS最容易被大家理解,我们也最关注,就不做过多的展开。
机器带宽问题,流量越大越容易遇到。假设CGI请求达到10W/S,通常它的请求存储不是一次,当时我们的常规业务请求存储高达7-8次,包括查询活动配置、查询用户参与记录、查询其他关联信息等。这个10W/S就意味存储端需要承受大几十万每秒甚至百万每秒的量,如果一个存储占1KB或者大几百KB,在这个量级下存储数据就相当庞大了,使用百兆级别网卡显然就会遇到网络带宽瓶颈问题。
接下来说说存储大小,这里说的存储大小通常不是指硬盘的存储大小,而是指内存,它也比较容易遇到瓶颈。大多数公司有使用Redis类的Key-value存储,也有部分公司使用自研的Key-value存储,通常写入的数据会先写入内存,再通过定时淘汰机制,同步到本地的SSD或者磁盘。在大流量场景下,内存很容易被写满,如果没有事前评估,活动当天几百G的内存会瞬间被写满。
还有一个不得不提的内容就是静态资源,它是我们网页请求的图片、JavaScript和CSS样式,通常它的瓶颈不在系统本身。一般情况下,如果不考虑带宽的限制,在一台配置很普通的机器上搭建一个Nginx来压测静态资源的请求性能,可以轻松达到数千QPS。但是,如果考虑到静态资源的大小,加上单个用户打开一个页面就会请求很多静态资源的实际情况,请求的静态资源的大小很较容易就超过1M了。这时第一个撑不住的就是机器的出口带宽,而不是静态资源服务器本身的性能。
针对静态资源问题,我们有常用的应对方法——离线包推送机制。在活动开始前几天就把静态资源推送到活跃用户的的手机里,这样活动当天九成以上的流量都是本地的,他根本没有发起网络请求,以此来解决CDN的请求压力,解决带宽问题。
完成扩容只能说在理论上没问题,接下来说说高可用架构建设的关键要点。概括起来就是几个点:过载保护、柔性可用、容灾建设、轻重分离、监控体系、数据对账。
监控体系很重要,我们需要探知这个系统处于什么状态,并且出现问题时需要一些方案去应对它。
前面的分享提到过一个问题,有些接口日常也就2000-3000/S的量,没有那么多的资源和精力扩到10万/s, 那怎么办呢?
关于系统链路架构优化,我们常用的方式有三种,第一种是异步化,这个好理解,暂时没有这个能力执行完,就放到消息队列里慢慢消化。第二种是内存级的缓存,把需要访问的数据提前放到内存级的Cache里。第三种是服务降级,这个比较常用,就不做过多介绍了。 通过这些方法就可以把链路的性能提升起来。
关于过载保护它的的核心目标是部分不可用至少比彻底不可用户要强,主要的实现手段是进行分层流量控制。这些层级包含推广层、前端层、CGI层和后端服务层。针对推广层,只能在最坏的情况使用,因为它要对广告进行下架处理,不到万不得已,公司也不愿意采用这个方式,它也会影响活动的业务效果。针对前端层,可以实现随机弹出倒计时,限制用户1分钟内不能发起新的网络请求,有助于削平流量峰值。其他流量控制层的实现大同小异,不做过多的展开。
关于部署,我们应该把重要的核心的一些操作给保护起来。例如,在上述案例中,比较重要的是发货,它不应该被其他操作影响。如果查询操作不可用,用户再刷新一次页面就好了,但如果发货不到账,用户会直接投诉,所以上述案例就把发货操作拆出来独立部署了一套异步发货集群。
柔性可用我们应该怎么实现呢?
一般有两个方向,第一种是设置超短的超时时间,例如,给安全接口设置超时时间为30毫秒,假设那天安全接口真的挂了,30毫秒对于一个百毫秒级别的请求来说,用户也不太能感知到,活动的体验还是好的。另外一种,就是直接不等网络回包的方式,例如UDP服务。
在容灾建设中,我们要大胆提出假设,假设各个核心的服务和存储都可能挂掉,根据假设去制定对应的容灾和解决方案。比如应对机房停电、网络故障,我们可以做跨机房跨网络部署,这样即使电信光纤被挖断,服务还能保持基本正常。另外要做好应急预案,把所有关键节点故障的假设对应的处理方案全部做成开关模式,避免在活动当天还要去跑日志和写处理脚本。
针对监控与对账,我们必须建立多维度的监控体系,在流量峰值的压力下部分监控可能会出现问题,多维度的监控能帮助我们探知系统的真实状态,有助于我们采取正确的应对措施。
关于对账能力,假如发货失败了,当天值班的同学需要看跑线上日志和写补发脚本,那么半个小时、一个小时就过去了。值班同学通常都比较忙,所以最好的方式就是提开发好,当天如果发现问题,它就能自动去检测失败的日志并且进行补发。
大规模流量活动的实践案例
前面讲了比较多关于大流量活动的架构准备工作,这一部分我们讲讲具体的实践案例。
第一个案例是某年某业务的春节红包活动,尽管前期做了非常多的准备,活动上线时还是出现了一系列问题,但好在都在我们的预案之内,算是有惊无险。
事后我们也进行了反思回顾,出现那么多问题是为什么?首先就是没有遵循3倍扩容原则,再比如说链路梳理没有到位,下面就是我们的一些总结内容。
根据我们的过往经验,最危险的活动反而不是大型活动,因为为了确保大活动的顺利开展,公司会抽调很多骨干参与进来,即使出现问题通常也是小问题。而中小型活动的流量不会太大,没有什么威胁。最容易出问题的恰恰是那些中大型活动,接下来详细讲讲另一个中大型活动案例。
在某游戏的年度活动项目中,我们做了一些评估和准备工作,但不可能对待每个活动都像对待春节红包活动那样。当时我们派了2个人看了一下,各个线上模块团队负责压测各自的模块,都反馈没问题,活动就上线了。活动当天凌晨我被电话叫醒,系统出问题了,当时有很多用户已经无法参加活动了。
当时的Web系统是基于PHP和Apache,采用Apache的多进程模式(perfork)。一个后端服务响应时间一般在几十毫秒,一个worker进程1秒钟应该可以处理十多个请求,但到了高峰时期,在大流量的压力下,后端服务的响应时间从几十毫秒飙到了几百毫秒甚至是1秒。这个时候1个wrker进程1秒钟只能处理1个请求,无法及时处理最终导致请求堆积。我们趁着玩家在凌晨的休息时间去做了不少补救工作,包括降级及其他的动作,经过一个通宵的努力,用户可以正常进入活动了,但还没有完全解决。第二天下午我们又到公司继续,48小时连轴转重新发版后终于解决了问题,安稳度过了那次活动危机。这种大中型的活动最容易出问题,因为通常我们不会给与足够的关注度。
总结一下大流量活动前期需要做的一些总体的经验和流程,首先是相对合理的评估&梳理方案,再进行架构上的优化和调整,最后就是整理紧急预案。
我连续参加过三年春节红包活动活动,每一年都提前大概一个多月召集核心骨干来出具方案,每年都做很多准备,可每年都有一些小问题。
反思为什么投入大量人力物力资源,系统还是出问题? 究其本质,我觉得主要是三个方面:第一很多时候测试环境、甚至性能环境无法代表真实的生产环境;第二,各个局部高可用不代表整体高可用;第三是系统的成长性,复杂的业务系统在持续迭代、膨胀,今天没性能问题,不代表明天没问题。
想让生产环境的性能获得保证,不出问题,从这个点出发我们有一个明确的探索方向——常态化的生产环境性能测试。
大量的准备工作都是为了保证线上环境能够达到一定的性能指标,所以最好的方式就是直接在生产环境去做最真实的性能测试。比如选在凌晨两三点钟没有什么业务流量的时候执行性能测试,看系统能不能达到我的目标性能。
那想要真正做到在生产环境实施性能压测有哪些关键点呢?
主要有三点,测试流量识别、测试流量标识的传递以及最重要的测试流量隔离。做生产环境压测避免不了涉及一些写入请求,如果把大量虚假的数据写入生产环境数据库肯定不合适,后续还要去做数据清理。所以只要前面提到的三个关键点能够解决,在线性能测试就能实现。
这是我们基于JAVA做的一套性能测试平台,因为我们可以在JAVA有中间字节码这一层做增强,通过agent探针在不侵入用户业务代码情况下去做一些控制。正式流量保持不变,我们会对压测的流量做一些控制。举个例子,比如说系统有一个mysql数据库,我们就会在线上建一套影子数据库,跟原来数据库一样,只是数据可能少一点或是空的(只有表结构)。当有流量进来时agent就会进行识别,识别出压测流量时,agent会把连接的数据库替换成影子数据库。 无论是消息队列还是Cache存储都可以用类似的方式来实现。
另外我们还做了一个东西叫挡板,它是干嘛的呢?假设有个第三方的短信接口,业务流量去调用这个接口是可以的,但测试流量直接去调用肯定是不行的,因为第三方不可能配合你做一个影子发短信的系统。挡板的作用就是通过agent来实现一个mock,在调用第三方接口时直接返回,防止测试流量调用第三方接口时对生产数据产生影响。
这是数列的生产环境全链路性能测试的方案,在此基础上我们还研发了链路自动梳理的功能。对于复杂业务以及复杂系统,它内含很多个微服务节点和业务节点,没有人能完全搞清楚整个链路的所有的环节。我们可以通过agent探针,利用技术手段搞清系统链路流向,协助进行链路梳理,与业务相结合就能梳理出业务调用的链路。结合E2E巡检平台不仅能够知道这条链路的性能如何,还能定位到具体环节的具体性能瓶颈,方便及时准确地进行性能调优,梳理业务链路视图,最终实现系统的高可用目标。
以上是关于大促流量激增,通过什么手段提升系统的高并发高可用性?的主要内容,如果未能解决你的问题,请参考以下文章