并发请求量小的单体项目可以借鉴这种方式;分布式并发高的项目建议还是用中间件来限流,最好不要让这种请求进入项目中
代码编写
自定义一个注解类
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) public @interface AccessLimit { int seconds(); int maxCount(); boolean needLogin() default true; }
接着就是在Interceptor拦截器中实现:
import com.tuniu.data.common.AccessLimit; import com.tuniu.data.common.RedisUtil; import org.springframework.stereotype.Component; import org.springframework.web.method.HandlerMethod; import org.springframework.web.servlet.handler.HandlerInterceptorAdapter; import javax.annotation.Resource; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.OutputStream; @Component public class AccessLimitInterceptor extends HandlerInterceptorAdapter { @Resource private RedisUtil redisUtil; @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { //判断请求是否属于方法的请求 if (handler instanceof HandlerMethod) { HandlerMethod hm = (HandlerMethod) handler; //获取方法中的注解,看是否有该注解 AccessLimit accessLimit = hm.getMethodAnnotation(AccessLimit.class); if (null == accessLimit) { return true; } //获取注解中的参数 int seconds = accessLimit.seconds(); int maxCount = accessLimit.maxCount(); boolean needLogin = accessLimit.needLogin(); String key = request.getRequestURI(); /* //如果需要登录 if(login){ //获取登录的session进行判断 //..... key+=""+"1"; //这里假设用户是1,项目中是动态获取的userId } */ //自定义 key String ak = "" + key; //从redis中获取用户访问的次数 Integer count = (Integer) redisUtil.get(ak); if (count == null) { //第一次访问 redisUtil.set(ak, 1, 5); } else if (count < maxCount) { //加1 redisUtil.incr(ak, 1); } else { //超出访问次数 render(response, "请求失败"); return false; } } return true; } private void render(HttpServletResponse response, String cm) throws Exception { response.setContentType("application/json;charset=UTF-8"); OutputStream out = response.getOutputStream(); out.write(cm.getBytes("utf-8")); out.flush(); out.close(); } }
把Interceptor注册到springboot中
import com.tuniu.data.Interceptor.AccessLimitInterceptor; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; @Configuration public class WebConfig extends WebMvcConfigurerAdapter { @Autowired private AccessLimitInterceptor accessLimitInterceptor; @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(accessLimitInterceptor); } }
在Controller中加入注解
import com.tuniu.data.common.AccessLimit; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController() @RequestMapping("/accessLimit") public class AccessLimitController { @AccessLimit(seconds = 5,maxCount = 4) @GetMapping("/accessLimitMethod") public String accessLimitMethod() { return "我访问了成功了"; } }
备注上 redis 的配置
application.yml:
#通用配置 spring: profiles: active: prd redis: host: ${redis.host} password: ${redis.password} #Redis服务器连接密码(默认为空) port: 6379 #Redis服务器连接端口 jedis: pool: max-active: 8 #连接池最大连接数(使用负值表示没有限制) max-idle: 8 #连接池中的最大空闲连接 # max-wait: 8 min-idle: 0 #连接池中的最小空闲连接 max-wait: 1000 #连接池最大阻塞等待时间(使用负值表示没有限制) timeout: 50000 #连接超时时间(毫秒)