说下自己对限流的理解
1.先说下java多线程中的例子
- 使用synchronized/ReentrantLock每次只有一个线程执行;
- 使用Semaphore信号量允许一定量的线程同时执行;
- ReentrantLock.tryLock(10000, TimeUnit.MILLISECONDS); 限制时间10s
- 1/2的情况会等待拿到锁;1无法中断,2能中断和限制量;3有个超时
- 限流就是限制对外服务并发量的。就像景区入口高峰的时候排队加等待。主要任务保证系统稳定和核心业务服务不挂!线程用锁是保证数据处理的原子性的,当然限流的处理手段是有原子性的。
2.限流的实现方式
2.1为什么要限流
对外提供服务的web接口接收请求后会有个线程池分配一个线程执行业务的逻辑处理,但是系统的处理能力是有限的线程不能无限制的开吧,为了不让服务挂掉处理请求控制在一个合理的范围内需要对访问进行一些限制上的处理,当然可能会说线程池可以有排队和拒绝策略但是这些一方面是容器的实现,另一个方面灵活性例如其中的一个常用服务卡了所有的请求都打在这一个服务上影响系统没能力处理其他服务了严重点整个系统挂了。还有个限流是反向的我们请求对方的接口对方限流了,我们这里也需要控制调用频率。
2.2限流触发后的操作
- 方式1:对于超量的访问直接打回返回错误码
- 方式2:排队/或者限制重试
- 方式3:降级区分核心业务调用和非核心丢弃
2.3代码怎么控制访问
- 直接想到的我给服务里面直接加上面的锁去控制,请求过多挤压服务会爆掉的锁那里有个谁去中断它和响应中断问题。
- (synchronized 是不可中断的;Lock 可以中断(lock.lockInterruptibly();)也可以不中断;Semaphore信号量在acquire过程中如果线程被中断会抛出中断异常,想忽略中断继续等待可以调用acquireUninterruptibly),对业务侵入性太太强。
- 或者因为是web我在过滤器拦截服务入口那里控制,但是通用性不太方便。
- 用AOP切面去控制了@Aspect很容易做到这点的。
- 另外限流这个东西主要是量提前控。
2.4访问量怎么控制及一些工具
-
先提下单机和集群我感觉区别就是这个控制是否是所有系统都能感知的及原子性,所以一般集群或分布式控制都要借助第三方的公用服务(第三方实现了原子性那么你的服务器集群就有了原子性)。
-
我想的计数器:需要一个原子操作的数据控制,那用Semaphore信号量的tryAcquire的一些方法或者Atomic类去计数处理,也可以借用redis的incr\decr实现,reids可以实现集群的控制。缺点量的控制吧;另外这个东西需要请求逻辑处理完后需要操作
-
计数器:一段时间内的总量不超。缺点上个时间段结尾,和下个时间段的开始流量激增那么系统的压力非常大
-
滑动窗口:单位时间的划分,说的是例如窗口1s每隔1s移动一次窗口,,redis的zset(时间戳值,ZREMRANGEBYSCORE删除1s前的数据,ZCARD统计)缺点窗口期内的访问量是固定的,中间有一段用完了后面就是空窗期异常需要精细处理,与计数器的区别解决了临界问题
-
令牌桶:固定时间内的令牌产生速度是固定的放到桶里面,主要控制流入,保护系统,控制访问频率;能应对突发流量因为没用的令牌会累积到最大值,,Gava的RateLimite(支持预热功能)。
-
漏斗限流:水池的水有流出速度也有注入流速,主要不要理解错了流量是进的控制流出 (我之前理解的是拿令牌有限制刚好相反),主控制流出,第三方有限流,调用的时候控制;当容量低的时候也能应对一定量的突发情况毕竟有个容量 。redis-cell
bash
127.0.0.1:6379> CL.THROTTLE key 15 30 60 1
1) (integer) 0 # 0允许,1拒绝
2) (integer) 16 # 容量,等于指令第二个参数 +1
3) (integer) 15 # 剩余量
4) (integer) -1 #-1表示正常放入,0表示已满,正数表示几秒可放入(单位:秒)
5) (integer) 2 # 多长时间漏斗完全漏空(单位:秒)
7.工具的话上面提到的还有阿里的Sentinel