削峰、限流
削峰限流要解决什么问题呢,要解决OrderController中create(),下单这个方法,也可以称之为前端的接口,下单时流量过大的问题 虽然说用了异步扣减库存,但是承载力是有限的,如果流量过大,服务器撑不住 另外,秒杀商品有限,将有限的人放进来就可以了,不用把所有参与秒杀的人都放进来 那么怎么限制
我们把业务接口叫做交易,怎么解决流量问题呢?把交易之前的验证迁移出来,单独作为一步 为什么?交易只是解决交易的问题,100个库存只放1000个人进来,而且是有权限的进来,他们来了就能交易,因为已经验证过了 现在限制验证环节的流量,通过验证环节来削减流量,而不是在交易环节,因为都已经挤进来了
验证成功了,就能直接交易吗?也不是,还要给用户颁发一个令牌,有这个令牌才可以去交易;在交易环节,就看有没有这个令牌,有令牌可以交易,没有出去
但是验证成功了就直接发令牌吗,100个商品,只需要让进来1000个人就足够了,1000个人入围去抢就可以了,所以要限制令牌的个数; 所以在中间加一个大闸,大闸就是一个参数,每次我们看这个参数,比如说这个商品开放的流量是1000,那么初始化这个大闸就是1000,颁发一个令牌就减一,最终颁发1000个令牌就行了 所以当用户验证通过的时候,需要看大闸有没有余量,有就颁发令牌,没有退出
有了令牌就能交易吗?也不是,因为有可能商品很多,那么大闸也很多,所以大闸相当于不能起到限流的作用了,因为基本上人人都会得到一个令牌;这个时候,就得想别的办法? 所以需要去限流,避免单机挂掉,所以需要加限流器,限制单机的TPS(每秒处理多少请求);
那么有了限流器就可以了吗?也不见得,因为限流器限制的流量肯定是按单机比较高的值来限制,比如服务器承载力最大是10000,限流器就写10000甚至多一点,因为想让服务器发挥最大能耗,所以往往写的是服务器接近极限的值; 有可能某些线程由于某些原因阻塞,被卡住了,可能交易环节就变慢了
为了让交易环节有一个缓冲,所以加了一个队列来处理,队列就是线程池,里面有多个线程;超过线程池的承载能力,还有等待队列可以等待; 本来交易环节是单线程处理,现在相当于提交给线程池,多线程处理,线程池处理能力肯定更强,可以复用一些线程,可以利用CPU多核的优势,并发处理,把单线程处理的瓶颈拓宽了
上面都是发送请求以后做的限制,那么能不能在请求之前做限制?我们在申请令牌之前,加验证码,可以平滑流量,因为秒杀的时候,很多人都在点下单的按钮,压力很大;所以加上验证码,要求输入验证码,输入验证码需要几秒钟,所以我们就实现了把一秒1000万的流量平滑到三五秒
总结,先提交验证码,去抢令牌,如果手速慢了,令牌可能就没抢到;即使抢到了,如果商品很多,服务器承载力有限,就加了一个限流器,限制服务器TPS,不让服务器挂掉,哪怕很多人失败,把很多请求拒绝掉,重新来一遍,也不会让服务器挂掉;这时候单机服务器达到了瓶颈,服务器处理很慢,所以加了一个线程池,缓冲一下,这样单机的运行就会比较健康,不容易挂掉 再加上是分布式部署,有很多服务器,每个服务器承载一些流量,多一点服务器流量就大了
验证码用easy capcha 大闸用一个变量,在redis中,promition:gate:161 XXX 限流器是用guava中的RateLimiter,限制的是1s的流量,不是总的流量,用的是令牌桶算法 队列就是线程池
令牌桶算法
限流,限制单机的TPS,我们用的是RateLimiter,限制的是一个比例,一个速率,不是限制一个死的流量,而是限制1s能处理多少,项目里设置的是1000; 限流器底层实现的时候,采用的是令牌桶算法;还有一种漏桶算法
限流器管理了一个容器,这个容器叫做令牌桶,令牌是字符串之类的东西;令牌桶容量是有上限的,假设有100个令牌 令牌桶算法的关键是,有一个令牌生成器,不断的生成令牌 1.一开始令牌桶中没有令牌,当我们初始化限流器的时候,内部会初始化若干个令牌,不是满的,比如10个, 2.然后令牌生成器每个一段时间,就生成一些令牌,放在令牌桶中,如果满了就丢弃多余的令牌。
限流器初始化以后,客户端向服务器发请求,被限流器拦截,限流器看令牌桶有没有令牌,如果没有了,那么就拒绝客户端的请求,给一个错误提示;如果获取到了令牌,令牌数减一,就可以访问业务组件了
漏桶算法
有一个漏桶,桶中装的是水滴,比如这个桶容量是100,那么就是装100滴水;漏桶直接对接业务组件; 漏桶关键是: 1.每秒会漏出若干滴水,漏出的水给业务组件,10滴水就是10个业务请求,不管现在桶中有多少滴水,每秒我只漏10滴水给业务组件,保证业务组件每秒只能处理10个业务请求 2.注水时水满则溢
客户端去访问限流器的时候,一个请求认为是一滴水,限流器尝试往桶中加水,桶的容量是有限制的,如果满了,拒绝请求;如果没有满,就加入一滴水;加进去的水不是立刻给业务组件,桶和业务组件的关系永远都是每秒出十滴水,没有到整秒,不会被处理,可能先到的还会先处理
区别
漏桶算法业务组件承担的压力是固定的,图中例子,每秒只处理10个请求,处理速率恒定,不可能出现爆发的情况,这种方式很稳健 令牌桶算法可能出现爆发的情况,有可能一开始没有请求,但是令牌生成器会一直生成令牌,导致桶满了,而在某一时刻,请求爆发,会把令牌全都获取到,导致服务器在某一刻压力很大,但是在后续时刻因为每秒生成的令牌是有限制的,后续的压力就稳定了;令牌桶算法可能出现业务高峰和业务低峰的情况。 如果追求服务器速率永远恒定,用漏桶算法;如果希望服务器某些时刻可以处理突发流量,但是不能长期的,就用令牌桶 秒杀的时刻一大波流量涌入,我们希望在这一刻处理比较多的请求,下面马上恢复到常态,所以用令牌桶
防刷
削峰限流都是正常的流量 防刷不是写几行代码就能解决的,往往是整个系统级别的,甚至是更高级别的一个处理,安全级别的处理,防刷就是防黄牛;黄牛可能用几个设备几十个设备反复刷,把优惠力度最大的商品抢到;即便是没有抢到,因为你已经做了限制,不断刷会让正常流量进不去接口中。 黄牛不太好防,防不胜防,因为双方都在不断进化
需要了解防黄牛的策略,往往企业都是将多个维度的策略组合再一起使用;而小的企业没有精力做,所以一般是购买这种服务,总体来说有几种主流的方式: 防黄牛就是防止黄牛不断刷我们的服务器,导致崩溃或者让正常流量无法进入
- 从用户角度来限制,比如下单这种借口,用户需要登录才能下单,这种接口适合用这种方式;而查询不用登录,不适用;服务器判断当前用户(通过cookie或者token)固定时间内访问了这个接口多少次,存到redis里;
但是这种方法很局限,就像查询这种操作,限制不了;专业黄牛有很多账号 - 限制IP,限制IP固定时间内多少次访问;
但是可能黄牛会虚拟设备,虚拟出很多IP;而且这种方式还可能误杀,比如一个单位有很多人,但是对外只用一个公网IP访问,用这种方式的话就会误杀 - 现在更建议的方式,小规模的秒杀活动,可以放到web应用、网页中来做,大的活动都会用移动端,手机,手机支付环境要比网页安全很多;因为网页可以看到所有请求的过程,请求信息都是一目了然的;但是手机的APP是定制化的,不像浏览器,所有行业都是统一的,APP在编译的时候能进行混淆加密,反编译也不容易解密,里面数据也可以加密;相对来说,手机安全
一般大规模支付、秒杀,建议在手机端上进行,在手机终端上访问Server的时候,Server可以采集设备的指纹,就是设备的一些信息,包括硬件信息、软件信息、APP信息等,通过这些信息组合成指纹,唯一识别终端,然后分析终端行为,看是否可疑;根据终端活动,看是否可疑,如果正常,就颁发一个凭证,以后访问就正常访问,不受约束;如果可疑,那么就发起验证,通过验证码或者短信等手段(我们去外地登录,经常会出现这种情况) 但是这样也不是百分之百杜绝,黄牛可以用模拟器,设备牧场
|