Java干货丨限流常规设计和实例( 七 )

Guava RateLimiter总结
1 , Guava在对RateLimiter设计中采用的是令牌桶的算法 , 提供了普通和预热两种模型 , 在存储令牌增加和消耗的设计思路是计算函数积分的方式 。
2 , 对于第一个新来的请求 , 无论请求多少令牌 , 不阻塞 。
3 , 内部使用线程sleep方式达到线程等待 , 没超时时间会一直等到有令牌
4 , 令牌存储发生在请求令牌时 , 不需要单独线程实现不断产生令牌的算法
5 , 内部设计的类一些参数并不开放外部配置
漏桶
原理如图:

Java干货丨限流常规设计和实例

文章插图
 
我们有一个固定的桶 , 这个桶的出水速率是固定的 , 流量不断往桶中放水 , 进水速度比出水速度快的时候 , 可以在桶中有一个缓冲 , 可是到达一定量超出桶的容量 , 就会溢出桶 , 无法接受新的请求 。
这个思路不就是阻塞队列嘛 , 只要在消费的放保持固定速率即可 。
实现类似如下代码(示意使用):
public class LeakyBucketLimiter { BlockingQueue<String> leakyBucket; long rate; public LeakyBucketLimiter(int capacity, long rate) { this.rate = rate; this.leakyBucket = new ArrayBlockingQueue<String>(capacity); } public boolean offer(){ return leakyBucket.offer(""); } class Consumer implements Runnable{ public void run() { try { while (true){ Thread.sleep(1000/rate); leakyBucket.take(); } } catch (InterruptedException e) {} } }}和令牌桶允许一定突发请求时的高速率 , 以及空闲后降低速率不同的是 , 漏桶算法是必然保证速率不变的 。
最后
  • 限流必然带来性能损失 , 如何避免?
一个思路是监控系统的状态 , 比如cpu , 内存 , io等 , 依据这些信息来开关限流器 。
  • 实际场景中是单机限流还是分布式限流?
在分布式系统中 , 如果想用分布式限流 , 就需要公用存储的载体 , 比如redis , zk等 。还需要考量分布式存储载体失效 , 不能影响正常业务功能 。
  • 拓展
    本文并不是限流的全部 , 关于限流这里只聊到了相关的一些常规的算法 , 可以说是冰山一角 , 还有很多知识等待我们去探索 , 前路漫漫 。
    另外 , 后续会参考开源限流方案 Sentinel 和 Bucket4j 进一步研究实践限流的落地方案 。




推荐阅读