介绍
令牌桶算法:
一个存放固定容量令牌的桶,按照固定速率往桶里添加令牌;如下:
a.假设是2r/s,则每500毫秒添加n个令牌;
b.桶中最多存放x个令牌,桶满时,再添加会拒绝;
c.请求去获取令牌,如果令牌足够,允许通过;如果令牌不足,拒绝请求或放入缓冲区等待;
漏桶算法:
漏桶容量固定,按照固定速率流出水滴;如果桶是空的,不用流,如果桶满了,再流入水滴,则拒绝服务。
区别
令牌桶控制的是平均流入速率,速率可高可低,允许有突发请求;漏桶控制的是恒定的流出速率,从而平滑流入速率。
java实现
令牌桶算法:
package com.cvnavi.oa.report;
import org.apache.kafka.common.protocol.types.Field;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
public class TokenLimiter {
private ArrayBlockingQueue<String> blockingQueue;
//容量大小
private int limit;
//令牌的产生间隔
private int period;
//令牌每次产生的个数
private int amount;
public TokenLimiter(int limit, int period, int amount) {
this.limit = limit;
this.period = period;
this.amount = amount;
blockingQueue = new ArrayBlockingQueue<>(limit);
}
private void init() {
for(int i = 0; i < limit; i++) {
blockingQueue.add("lp");
}
}
private void addToken(int amount) {
for(int i = 0; i < amount; i++) {
//溢出返回false
blockingQueue.offer("lp");
}
}
/**
* 获取令牌
* @return
*/
public boolean tryAcquire() {
//队首元素出队
return blockingQueue.poll() != null ? true : false;
}
/**
* 生产令牌
*/
private void start(Object lock) {
Executors.newScheduledThreadPool(1).scheduleAtFixedRate(() -> {
synchronized (lock) {
addToken(this.amount);
lock.notify();
}
}, 500, this.period, TimeUnit.MILLISECONDS);
}
/**
* 先生产2个令牌,减少4个令牌;再每500ms生产2个令牌,减少4个令牌
*/
public static void main(String[] args) throws InterruptedException {
int period = 500;
TokenLimiter limiter = new TokenLimiter(8, period, 2);
limiter.init();
Object lock = new Object();
limiter.start(lock);
//让线程先产生2个令牌(溢出)
synchronized (lock) {
lock.wait();
}
for(int i = 0; i < 8; i++) {
for(int j = 0; j < 4; j++) {
String s = i + "," + j + ":";
if(limiter.tryAcquire()) {
System.out.println(s + "拿到令牌");
}
else{
System.out.println(s + "拒绝");
}
}
Thread.sleep(period);
}
}
}
漏桶算法:
package com.cvnavi.oa.report;
import org.apache.kafka.common.protocol.types.Field;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
public class TokenLimiter {
private ArrayBlockingQueue<String> blockingQueue;
//容量大小
private int limit;
//令牌的产生间隔
private int period;
//令牌每次产生的个数
private int amount;
public TokenLimiter(int limit, int period, int amount) {
this.limit = limit;
this.period = period;
this.amount = amount;
blockingQueue = new ArrayBlockingQueue<>(limit);
}
private void init() {
for(int i = 0; i < limit; i++) {
blockingQueue.add("lp");
}
}
private void addToken(int amount) {
for(int i = 0; i < amount; i++) {
//溢出返回false
blockingQueue.offer("lp");
}
}
/**
* 获取令牌
* @return
*/
public boolean tryAcquire() {
//队首元素出队
return blockingQueue.poll() != null ? true : false;
}
/**
* 生产令牌
*/
private void start(Object lock) {
Executors.newScheduledThreadPool(1).scheduleAtFixedRate(() -> {
synchronized (lock) {
addToken(this.amount);
lock.notify();
}
}, 500, this.period, TimeUnit.MILLISECONDS);
}
/**
* 先生产2个令牌,减少4个令牌;再每500ms生产2个令牌,减少4个令牌
*/
public static void main(String[] args) throws InterruptedException {
int period = 500;
TokenLimiter limiter = new TokenLimiter(8, period, 2);
limiter.init();
Object lock = new Object();
limiter.start(lock);
//让线程先产生2个令牌(溢出)
synchronized (lock) {
lock.wait();
}
for(int i = 0; i < 8; i++) {
for(int j = 0; j < 4; j++) {
String s = i + "," + j + ":";
if(limiter.tryAcquire()) {
System.out.println(s + "拿到令牌");
}
else{
System.out.println(s + "拒绝");
}
}
Thread.sleep(period);
}
}
}