参考:https://blog.csdn.net/lzw_2006/article/details/51789859
一、简单的guava平滑限流方案
public class RateLimiterTest {
public static void main(String[] args) throws InterruptedException, Exception {
RateLimiter rateLimiter = RateLimiter.create(2); //每秒往令牌桶投放2个令牌,每0.5s一个
CountDownLatch countDownLatch = new CountDownLatch(2);
for (int i = 0; i < 2; i++) {
new Thread(new Runnable() {
@Override
public void run() {
try {
countDownLatch.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
if (rateLimiter.tryAcquire(1, 488, TimeUnit.MILLISECONDS)) { //tryAcquire检测有没有可用的令牌
System.out.println("处理请求");
} else {
System.out.println("拒绝请求");
}
}
}).start();
countDownLatch.countDown();
}
}
}
分析:每秒往令牌桶投放2个令牌,即每0.5s 一个。两个线程同时获取令牌,第2个要0.5s才能获取到,但获取令牌超时时间为0.488s<0.5s不能获取到令牌。超时时间改为大于0.5s基本就能正常处理请求了。
二、基于Redis时间窗口分布式限流方案(自定义注解+aop)
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface AccessLimit {
int expire() default 1;
int limit() default 500;
}
@Aspect
@Component
public class AccessLimitAspect {
@Autowired
RedisTemplate redisTemplate;
@Before("@annotation(AccessLimit)")
public void deBefore(JoinPoint joinPoint) throws Throwable {
MethodSignature sign = (MethodSignature) joinPoint.getSignature();
Method method = sign.getMethod();
String methodName = method.getName();
AccessLimit annotation = method.getAnnotation(AccessLimit.class);
int expire = annotation.expire();
int limit = annotation.limit();
Long methodLimit = redisTemplate.opsForValue().increment(methodName);
if (methodLimit==1) {
redisTemplate.expire(methodName,expire,TimeUnit.SECONDS);
}
if (methodLimit>limit) {
throw new RuntimeException(methodName+"接口访问人数过多!");
}
}
}
三、基于Nginx+lua脚本的接入层限流方案,lua脚本不会玩,暂时就这样了,请参考https://blog.csdn.net/lzw_2006/article/details/51789859