防止接口同一时间内对一个接口进行频繁的访问,可以对接口进行限流。
1.自定义注解,用来标识需要限流的接口。
package com.springweb.demo.limit; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * 接口流量控制 */ @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.METHOD, ElementType.TYPE}) public @interface AccessLimit { /** * 多少秒内 * @return */ long second() default 2L; /** * 最大访问次数 * @return */ long maxTime() default 1L; long forbiddenTime() default 3L; String apiUrl() default ""; }
2.使用拦截器Interceptor对使用注解的接口进行限流处理
package com.springweb.demo.limit; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import lombok.extern.slf4j.Slf4j; import org.springframework.web.method.HandlerMethod; import org.springframework.web.servlet.HandlerInterceptor; import java.time.LocalDateTime; import java.time.temporal.ChronoUnit; import java.util.HashMap; import java.util.Objects; @Slf4j public class AccessLimitInterceptor implements HandlerInterceptor { private static HashMap<String, InterfaceInfo> accessMap = new HashMap<>(); @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { if (handler instanceof HandlerMethod) { HandlerMethod targetMethod = (HandlerMethod) handler; AccessLimit targetClassAnnotation = targetMethod.getMethod().getDeclaringClass().getAnnotation(AccessLimit.class); boolean isBrushForAllInterface = false; long second = 0L, maxTime = 0L, forbiddenTime = 0L; if (!Objects.isNull(targetClassAnnotation)) { log.info("目标接口方法所在类上有@AccessLimit注解"); isBrushForAllInterface = true; second = targetClassAnnotation.second(); maxTime = targetClassAnnotation.maxTime(); forbiddenTime = targetClassAnnotation.forbiddenTime(); } String uri = request.getRequestURI(); // 目标方法上的@AccessLimit注解 AccessLimit accessLimit = targetMethod.getMethodAnnotation(AccessLimit.class); if (!Objects.isNull(accessLimit)) { second = accessLimit.second(); maxTime = accessLimit.maxTime(); forbiddenTime = accessLimit.forbiddenTime(); if (isForbidden(second, maxTime, forbiddenTime, "登录用户唯一标识", uri)) { return false; } } else { if (isBrushForAllInterface && isForbidden(second, maxTime, forbiddenTime, "登录用户唯一标识", uri)) { return false; } } } return true; } private boolean isForbidden(long second, long maxTime, long forbiddenTime, String id, String uri) { String lockKey = id + "_" + uri; InterfaceInfo interfaceInfo = accessMap.get(lockKey); if (!Objects.isNull(interfaceInfo)) { // 还在禁用中 if (LocalDateTime.now().compareTo(interfaceInfo.getForbiddenTime()) <= 0) { return true; } // 判断两次接口访问的时间差是否超过限制 long diffSeconds = ChronoUnit.SECONDS.between(LocalDateTime.now(), interfaceInfo.getAccessTime()); if (diffSeconds > second) { accessMap.remove(lockKey); } } // 判断此用户访问接口是否已经禁用 if (Objects.isNull(interfaceInfo)) { // 还未被禁用 interfaceInfo = new InterfaceInfo(); interfaceInfo.setAccessTime(LocalDateTime.now()); interfaceInfo.setAccessCount(1); } else { if (interfaceInfo.getAccessCount() < maxTime) { interfaceInfo.setAccessCount(interfaceInfo.getAccessCount() + 1); } else { // 禁止访问 interfaceInfo.setAccessTime(LocalDateTime.now()); interfaceInfo.setAccessCount(0); // 设置禁止到的时间点 interfaceInfo.setForbiddenTime(LocalDateTime.now().plus(forbiddenTime, ChronoUnit.SECONDS)); accessMap.put(lockKey, interfaceInfo); return true; } } return false; } }
这里使用map处理 + InterfaceInfo.java对接口进行限流处理。也可以用redis进行处理。
package com.springweb.demo.limit; import lombok.Data; import java.time.LocalDateTime; @Data public class InterfaceInfo { private LocalDateTime accessTime; private LocalDateTime forbiddenTime; private int accessCount; }
3.对接口进行限流。
@AccessLimit使用默认属性。2s内只能访问一次,如果访问超过2次那么接下来的3s内拒绝这个接口的访问。
package com.springweb.demo.controller; import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport; import com.github.xiaoymin.knife4j.annotations.ApiSort; import com.springweb.demo.limit.AccessLimit; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; import org.springframework.web.bind.annotation.*; @RestController @RequestMapping("/hello") @ApiSort(1) @Tag(name = "实例功能") public class HelloController { @GetMapping("/helloString") @ResponseBody @Operation(summary = "输出hello", description = "hello接口", method = "GET") @ApiOperationSupport(order = 1) @AccessLimit public String helloString(){ return "hello, spring-web-demo."; } }
标签:java,spring,forbiddenTime,流量,interfaceInfo,second,接口,import,maxTime From: https://www.cnblogs.com/wang-liang-blogs/p/18039398