전류 제한이 있는 이유
시스템을 설계할 때 시스템의 예상 용량을 갖게 되는데, 시스템이 오랫동안 견딜 수 있는 TPS/QPS 임계값을 초과하면 시스템이 압도될 수 있습니다. 결국 전체 서비스를 사용할 수 없게 됩니다. 이러한 상황을 방지하려면 인터페이스 요청 흐름을 제한해야 합니다.
따라서 동시 액세스 요청 비율이나 시간 창 내 요청 수를 제한하여 시스템을 보호하거나 불필요한 리소스 낭비를 방지할 수 있습니다. 비율 제한에 도달하면 서비스를 거부하거나 대기하거나 대기할 수 있습니다.
현재 제한 배경
시스템에는 개방형 인터페이스이기 때문에 사용자가 인증 코드를 얻기 위해 지속적으로 요청을 보내는 것을 방지하고 인터페이스의 악의적인 스와이프를 방지하기 위한 인터페이스가 있습니다. 발생하지 않도록 최신 간단한 카운터 방법을 사용하여 현재 흐름을 제한하고 각 IP를 분당 하나의 요청으로 제한한 다음 비즈니스 로직을 통해 서로의 휴대폰 번호의 시간 창 제한을 판단합니다. 일반적으로 일부 인터페이스는 방문 횟수가 상대적으로 많아 시스템을 압도할 수 있으므로 트래픽 제한을 추가해야 합니다! 예: 플래시 세일 등...
구현 방법
1. 종속성 소개
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
2. @Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RateLimiter {
/**
* 限流key
*/
String key() default Constants.RATE_LIMIT_KEY;
/**
* 限流时间,单位秒
*/
int time() default 60;
/**
* 限流次数
*/
int count() default 100;
/**
* 限流类型
*/
LimitType limitType() default LimitType.DEFAULT;
/**
* 限流后返回的文字
*/
String limitMsg() default "访问过于频繁,请稍候再试";
}
3.제한된 흐름 section @Aspect
@Component
public class RateLimiterAspect {
private final static Logger log = LoggerFactory.getLogger(RateLimiterAspect.class);
@Autowired
private RedisUtils redisUtils;
@Before("@annotation(rateLimiter)")
public void doBefore(JoinPoint point, RateLimiter rateLimiter) throws Throwable {
int time = rateLimiter.time();
int count = rateLimiter.count();
long total = 1L;
String combineKey = getCombineKey(rateLimiter, point);
try
{
if(redisUtils.hasKey(combineKey)){
total = redisUtils.incr(combineKey,1); //请求进来,对应的key加1
if(total > count)
throw new ServiceRuntimeException(rateLimiter.limitMsg());
}else{
redisUtils.set(combineKey,1,time); //初始化key
}
}
catch (ServiceRuntimeException e)
{
throw e;
}
catch (Exception e)
{
throw new ServiceRuntimeException("网络繁忙,请稍候再试");
}
}
/**
* 获取限流key
* @param rateLimiter
* @param point
* @return
*/
public String getCombineKey(RateLimiter rateLimiter, JoinPoint point) {
StringBuffer stringBuffer = new StringBuffer(rateLimiter.key());
if (rateLimiter.limitType() == LimitType.IP)
{
stringBuffer.append(IpUtils.getIpAddr(ServletUtils.getRequest())).append("-");
}
MethodSignature signature = (MethodSignature) point.getSignature();
Method method = signature.getMethod();
Class<?> targetClass = method.getDeclaringClass();
stringBuffer.append(targetClass.getName()).append("-").append(method.getName());
return stringBuffer.toString();
}
}
4. 테스트를 위한 간단한 인터페이스 작성 @RestController
public class TestController {
@RateLimiter(time = 60, count = 1, limitType = LimitType.IP, limitMsg = "一分钟内只能请求一次,请稍后重试")
@GetMapping("/hello")
public ResultMsg hello() {
return ResultMsg.success("Hello World!");
}
}
5. 전역 예외 차단 @RestControllerAdvice
public class GlobalExceptionHandler {
private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class);
/**
* 业务异常
*/
@ExceptionHandler(ServiceRuntimeException.class)
public ResultMsg handleServiceException(ServiceRuntimeException e, HttpServletRequest request)
{
return ResultMsg.error(e.getMessage());
}
/**
* 系统异常
*/
@ExceptionHandler(Exception.class)
public ResultMsg handleException(Exception e, HttpServletRequest request)
{
return ResultMsg.error("系统异常");
}
}
6 . 인터페이스 테스트
1) 처음 보내면 결과가 정상적으로 반환됩니다
2) 1분 이내에 두 번째 보내면 오류가 발생하고 현재 제한 프롬프트가 표시됩니다
The 위의 인터페이스 전류 제한을 구현하는 AOP + Redis
솔루션입니다. 학습을 잃었습니까?
슬라이딩 윈도우 전류 제한 방법(카운터보다 더 엄격함), 토큰 버킷 등과 같은 다른 전류 제한 방법이 있습니다. 관심 있는 친구들은 이에 대해 알아볼 수 있습니다.
위 내용은 인터페이스 전류 제한, 모든 멋진 작업이 여기에 있습니다!의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!