黑名单机制的重要性
-
区分临时限流和永久限制:
临时限流:当请求速率超过设定的阈值时,系统会暂时拒绝请求,并记录被拒绝的次数。
黑名单机制:当被拒绝的次数达到预设的黑名单阈值时,系统会将该请求来源加入黑名单,并在一段时间内禁止其访问。 -
防止恶意攻击:
黑名单机制可以有效防止恶意用户或脚本持续高频次地访问系统,从而保护系统免受DDoS攻击或其他形式的滥用。
-
提高系统稳定性:
通过区分临时限流和永久限制,系统可以更平滑地处理流量波动,避免因突发流量导致系统崩溃。
import com.google.common.util.concurrent.RateLimiter;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
import java.util.concurrent.ConcurrentHashMap;
@Aspect
@Component
public class RateLimiterAspect {
private final ConcurrentHashMap<String, RateLimiter> limiters = new ConcurrentHashMap<>();
private final ConcurrentHashMap<String, Integer> blacklists = new ConcurrentHashMap<>();
private final ConcurrentHashMap<String, Long> blacklistExpiry = new ConcurrentHashMap<>();
@Around("@annotation(RateLimiterAccessInterceptor)")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
RateLimiterAccessInterceptor annotation = method.getAnnotation(RateLimiterAccessInterceptor.class);
String key = annotation.key();
double permitsPerSecond = annotation.permitsPerSecond();
double blacklistCount = annotation.blacklistCount();
String fallbackMethod = annotation.fallbackMethod();
// 检查是否在黑名单中
if (isBlacklisted(key)) {
return invokeFallbackMethod(joinPoint, fallbackMethod);
}
RateLimiter rateLimiter = limiters.computeIfAbsent(key, k -> RateLimiter.create(permitsPerSecond));
if (rateLimiter.tryAcquire()) {
return joinPoint.proceed();
} else {
blacklists.put(key, blacklists.getOrDefault(key, 0) + 1);
if (blacklists.get(key) >= blacklistCount) {
// 加入黑名单逻辑
addToBlacklist(key);
return invokeFallbackMethod(joinPoint, fallbackMethod);
}
return invokeFallbackMethod(joinPoint, fallbackMethod);
}
}
private boolean isBlacklisted(String key) {
Long expiryTime = blacklistExpiry.get(key);
if (expiryTime != null && System.currentTimeMillis() < expiryTime) {
return true;
}
// 如果黑名单过期,移除黑名单记录
blacklistExpiry.remove(key);
blacklists.remove(key);
return false;
}
private void addToBlacklist(String key) {
blacklistExpiry.put(key, System.currentTimeMillis() + 24 * 60 * 60 * 1000); // 黑名单有效期24小时
}
private Object invokeFallbackMethod(ProceedingJoinPoint joinPoint, String fallbackMethod) throws Throwable {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
Class<?> clazz = method.getDeclaringClass();
Method fallback = clazz.getMethod(fallbackMethod, signature.getParameterTypes());
return fallback.invoke(joinPoint.getTarget(), joinPoint.getArgs());
}
}
package cn.bugstack.types.annotations;
import java.lang.annotation.*;
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
@Documented
public @interface RateLimiterAccessInterceptor {
/** 用哪个字段作为拦截标识,未配置则默认走全部 */
String key() default "all";
/** 限制频次(每秒请求次数) */
double permitsPerSecond();
/** 黑名单拦截(多少次限制后加入黑名单)0 不限制 */
double blacklistCount() default 0;
/** 拦截后的执行方法 */
String fallbackMethod();
}
-
黑名单检查:
isBlacklisted(key):检查请求来源是否在黑名单中。如果在黑名单中且未过期,则直接调用fallbackMethod。
-
限流逻辑:
rateLimiter.tryAcquire():尝试获取一个令牌。如果成功,表示请求未被限流,继续执行方法;如果失败,表示请求被限流。
blacklists.put(key, blacklists.getOrDefault(key, 0) + 1):将被限流的请求次数加1,并记录在blacklists中。 if (blacklists.get(key) >= blacklistCount):检查被限流的请求次数是否达到预设的黑名单阈值。如果达到阈值,执行加入黑名单逻辑,并调用fallbackMethod方法进行处理。 -
加入黑名单逻辑:
addToBlacklist(key):将请求来源加入黑名单,并设置黑名单过期时间(如24小时)。
总结
黑名单机制在限流失败后仍然有其重要性,因为它可以区分临时限流和永久限制,防止恶意攻击,并提高系统稳定性。通过在限流逻辑中加入黑名单检查和加入黑名单的逻辑,可以更灵活地应对不同程度的流量冲击,保护系统免受滥用和攻击。