package com.geely.otaone.consume.config;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.stereotype.Component;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.atomic.AtomicInteger;
/**
* 限流器
*/
@Component
@RefreshScope
@Slf4j
public class Limiter {
/**
* tsp06接口下发可升级任务的速率,单位:次/秒
*/
private static int RATE;
/**
* 池子大小
*/
private static int TICKET_POOL_MAX_SIZE;
@Value("${com.alex.rate}")
public void setRATE(int RATE) {
Limiter.RATE = RATE;
}
/**
* 池子大小默认为 15s 的流量
*/
@Value("${com.alex.rate}")
public void setTICKET_POOL_MAX_SIZE(int RATE) {
TICKET_POOL_MAX_SIZE = RATE * 15;
}
/**
* 剩余可用名额
*/
private static AtomicInteger TICKET_LEFT;
private static Timer adderTimer;
{
if (adderTimer != null) {
adderTimer.cancel();
}
// 启动一个定时任务,每秒中向剩余可用名额中添加 RATE 个名额
adderTimer = new Timer(true);
int INTERVAL = 1000;
adderTimer.scheduleAtFixedRate(new TimerTask() {
@Override
public void run() {
TICKET_LEFT.set(Math.min(TICKET_LEFT.get() + RATE, TICKET_POOL_MAX_SIZE));
}
}, 0, INTERVAL);
TICKET_LEFT = new AtomicInteger(TICKET_POOL_MAX_SIZE);
}
/**
* 从限流器中获取名额
*
* @return 若有名额返回 true ,否则返回 false
*/
public synchronized boolean getTicket() {
if (TICKET_LEFT.get() > 0) {
TICKET_LEFT.addAndGet(-1);
return true;
}
return false;
}
}
经验积累:
- @RefreshScope 注解可以使 @Value 的值在 nacos 配置修改时自动刷新(懒加载的方式,即刷新的时机是该 bean 对象被使用时); 并且刷新时该 Bean 的构造方法会被重新调用;
- 如果向要将 nacos 配置的值读取进 static 变量中,可以使用 @Value 注解写在 setter 方法上;
- @Component 注解需要有 public 的构造方法才能正常使用。