2020-12-30

千万级流量框架设计

一:网站峰值QPS计算公式

峰值QPS=(日总PV数*80%)/(日总秒杀*20%)

即:一天中80%的流量集中在20%的时间内发生。

例如:pv=10000000;

          则峰值QPS=(10000000*0.8)/(24*3600*0.2)=463.

        理论上:如果一台服务器每秒能处理100个请求,那日pv千万的流量也只需要分布式5台服务器就能抗住。

 

二:核心的架构策略

           架构演进的过程:单机混沌状态--各自独立--集群化--分布式--多集群部署--异地部署

           集群:多个人干活,每个人干的活都一样。例如:三个厨师都需要干洗菜切菜炒菜的活。

           分布式:把活拆开一组人去做,大家协同完成,例如:一个人洗菜一个人切菜一个人烧菜。

       核心:拆分--队列--缓存--降级--限流

2.1:拆分

           拆分维度:系统维度,功能维度和读写维度。

           系统维度:商品系统,购物车系统,订单系统,优惠券系统。

           功能维度:例如优惠券系统,可以拆分为建券服务,领券服务和用券服务。

           读写维度:读的量非常大,比写的多的多。

2.2:队列

             核心:异步,平缓的处理

             2.2.1:队列案例1--流量削峰

                       流量削峰的由来:瞬间流量巨大,例如几万人抢购100件商品。就像洪水涌入。     

                       流量削峰的目的:让服务处理请求更佳平缓,波阿虎服务器资源。

                       直接调用模式:多个调用者一起请求,流量大的时候产生调用高峰,被调用者压力大,有崩溃风险。

                       消息队列模式:所有调用请求都去排队,即使高峰期也不会对被调用者直接产生影响。

                       流量大的情况下核心转变思想:转变为异步的队列处理模式。 

2020-12-30_第1张图片

             2.2.2:队列案例2--分布式事务(可靠消息模式)

                   案例场景:用户成功下单后,为用户增加相应的积分。

                   直接调用的问题:如果积分系统故障,订单系统不知道,已经生成的订单不好处理。

                  分布式事务:需要保证订单系统,积分系统的执行结果一致,都成功,或者都失败。

                  2020-12-30_第2张图片

                  改进:

                  使用消息队列,从同步调用改为异步调用,可以不关心积分系统的状态,保证最终一致性。

                  问题:订单系统应该是先写数据库还是先发消息?

                  2020-12-30_第3张图片

                  改进:

                 将写消息和创建订单放在同一个事务中,把消息写入本地数据库的“消息表”中。这样保证了创建订单和创建消息的一致性。 

                 问题:如何把消息发送到消息队列?

2020-12-30_第4张图片

          改进:

           通过一个后台程序“定时任务”去发送消息,并修改“消息表”中的数据状态。

           问题:消息会不会重发?消息重发了怎么办?(此时需要积分系统负责保证幂等性)

2020-12-30_第5张图片 改进(最终方案):

                  1:上游(订单)系统要保证信息不丢,是通过本地消息表后台定时程序来实现的。

                  2:下游(积分)系统要保证消息不重复处理,也就是保证幂等性,可以通过判重表来实现。

                (幂等:多次调用同一请求同一参数的处理的结果一样)

                  适用场景:

                  分布式事务的提交或者回滚只取决于事务的发起方,也就是无需回滚。

   2020-12-30_第6张图片                 

2.3:缓存

             类型:

             客户端缓存:浏览器/app客户端

             网络缓存:CDN

             接入层缓存:NGINX代理缓存。

             应用层缓存:redis。

             缓存的读写策略:

             Cache Aside策略  and  Read/Write Through策略:

             先更新缓存,还是先更新数据库?

             假设先更新数据库:

             现象:A先更新数据库,由于网络等原因在还没有更新缓存成功的同时,B已经更新了数据库和缓存,此时A再更新缓存,就导致数据库和缓存中的数据不一致。             2020-12-30_第7张图片

             假设先更新缓存:

      现象:如果先更新缓存,再更新数据库,可能失败回滚,导致数据库和缓存中的也不一致。       2020-12-30_第8张图片

            所以,综上所述先更新谁都不妥。

            2.3.1:Cache Aside 策略:

            更新数据时不更新缓存,删除缓存中的数据。

            读取数据时,如果缓存中没有数据,从数据库中读取,更新到缓存中。

             2020-12-30_第9张图片

            cache aside策略的读写流程:

            读:读请求-先读缓存--如果命中,直接返回数据---如果没有命中,读取数据库,写入缓存,返回数据。

            写:写请求-先写数据库--再删除缓存。

            问题:在写的时候可不可以先删除缓存?不可以!(因为先删缓存,再更新数据库,可能更新数据库失败,这样后续的读就增加了访问数据库的压力)

            2020-12-30_第10张图片

            

            2.3.2:Read/Write Through 策略:

             核心原则:用户只与缓存打交道,由缓存和数据库通信,写入或者读取数据。

             注意,这里有个关键角色,就是这个”缓存组件“,有点像仓库管理员。

             read/write through 策略的读写流程:

2020-12-30_第11张图片

             读:读请求--读缓存--如果命中,直接返回数据--如果不命中,读数据库,写缓存,返回数据。

             写:写请求--读缓存--命中,直接写数据库--未命中,先写缓存,再由缓存组件同步写入数据库。

            2.3.3:缓存的雪崩

             缓存的雪崩:大量数据同时失效 导致  数据库压力过大

             解决方案:

              1.为有效期 增加随机值

              2.使用高可用 分布式缓存

              3. 热点数据永远 不过期。

              4.在缓存失效后,通过加锁或者队列来控制读数据库的线程数量。

            2.3.4:缓存的穿透

             缓存的穿透:缓存中没有,需要访问数据库,这样是缓存被穿透了。穿透后就需要查询DB,量大的话就会压垮数据库。

             解决方案:

              1.缓存,空值

                 缓存“空值”的处理场景(正常访问的场景):

                         正常请求不存在的数据,可能其他请求也会读取这个数据,为防止多次无效访问数据库,我们可以把这个空值也缓存起来,用户下次请求时,就直接返回空。

2020-12-30_第12张图片

              2.采用布隆过滤器。

                  布隆过滤器的处理场景(恶意攻击的场景):

                          大量恶意请求不存在的数据,即使缓存空值也无效,大量请求读取数据库,需要使用布隆过滤器,帮助我们在不查数据库的情况下就知道此ID是否存在,把恶                    意请求直接过滤掉。

2020-12-30_第13张图片

            2.3.5:布隆过滤器

                        1970年布隆提出了一种过滤器的算法,用来判断一个元素是否在一个集合中。

                         这种算法由一个二进制数组和一个Hash算法组成。

                         优势:省空间,性能高!

                         不足:有误判,不能删除。

                         2020-12-30_第14张图片

                         如何使用布隆过滤器来解决缓存穿透的问题呢?

                         1:写入数据时,更新布隆过滤器

                         2:查询数据时,先查询布隆过滤器是否存在,如果不存在,直接返回空,如果存在,再去查询数据库。

                         图:

                        布隆过滤器的不足:

                        2020-12-30_第15张图片

2020-12-30_第16张图片

                    

2.4:降级

            功能降级:电商平台很普遍的“推荐功能”,可以提升销量,但不是购物核心流程。

                               在系统压力大时,可以降级,改为默认内容。

            写服务降级:不更新数据库,只更新缓存,把要写的数据放到消息队列,流量高峰过了以后,把消息队列里的数据回放到数据库。

2020-12-30_第17张图片

            降级的定义:

             整体负载,流量>阀值

             为了保证重要的服务能正常运行,1:拒绝部分请求,2:延迟和暂停一些不重要的服务,任务。

           降级的作用:

            降级是系统保护的重要手段,保证系统的高可用。简单理解降级就是“丢车保帅”,保证系统核心功能的正常。

          降级的方式:

          自动降级:

                 程序调用时发生问题时,自动降级。

                 超时降级:对于非核心服务,如果长时间响应慢,就可以自动降级。

                 限流降级:当触发了限流阀值时,可以使用暂时屏蔽的方式进行降级。

                 统计失败次数降级:在一个时间段内,调用失败率超出阀值,自动降级。

          手动降级: 

                 使用开关配置,对系统中可降级的服务都设置好开关项。

                 配置文件实现开关配置:

                 适用于系统部署结构简单的场景。

                 系统自动监控配置文件的变化

                 配置文件变化后重新载入。

                配置中心实现开关配置:

                适用于分布式系统,有统一配置中心。

                服务开关在配置中心中定义

                在配置中心界面修改相应参数,自动通知相应服务。

                配置中心实现技术:zookeeper,redis,consul,etcd。

                降级后的处理方案:

               使用默认值;兜底数据;缓存数据;排队页面;无货通知;错误页面。。。。

                          

2.5:限流

            对于系统,在应对高性能压力的场景时,限流已经成为了标配技术解决方案,保证了系统的平稳运行。

            限流就是对请求进行限制,例如某一个接口的请求限制为100个每秒,对超过限制的请求则不处理。

           保护系统的手段:

           缓存:提升系统的访问速度,增大系统处理能力。

           降级:暂时屏蔽,过后打开。

           限流:对稀缺资源限制请求量,限速,拒绝服务。

           四个常用的限流算法:

           1.固定时间窗口:

              对每个时间窗口内的请求进行计数,如果计算器超过了限制数量,则本窗口内后续的请求都被丢弃。当时间达到下一个窗口时,计数器重置。

              缺点:由于限流过于粗糙,无法应对两个时间窗口临界时间内的突发流量。例如:例如每秒限流3个请求,但前一秒内3个请求是在后半秒来的和后一秒内3个请求是在前半秒内来的,此时无法应对两个时间窗口临界时间内的突发流量【实际在这后半秒+前半秒的一秒时间内,请求数超过了3个达到了6个。】

             2020-12-30_第18张图片

2020-12-30_第19张图片

          2:滑动时间窗口

               记录在时间窗口内每个接口请求达到的时间点,判断当前时间窗口内的接口请求数是否小于限流值。

               2020-12-30_第20张图片

          3:令牌桶

               令牌以固定速率生成,如果令牌桶满了则多余的令牌直接丢弃。

                当请求达到时,会尝试从令牌桶中去令牌,取到了令牌的请求可以执行。

                 如果桶空了,那么尝试去令牌的请求会被直接丢弃。

                  2020-12-30_第21张图片

           4:漏桶算法

                 漏桶容量固定,按照固定速率流出请求

                 可以任意速率流入请求

                 如果流入过快,会溢出对其请求。

2020-12-30_第22张图片

          限流形式:

          1:单机限流,每个应用实例自己本机实现限流。

          2:分布式限流,一个应用集群统一做限流。

2020-12-30_第23张图片

 

 

 

 

         

        

                       

 

 

 

 

 

 

                  

 

 

 

    

     

 

 

 

你可能感兴趣的:(架构,设计原则,web高并发,负载均衡服务器,java)