并发请求量小的单体项目可以借鉴这种方式;分布式并发高的项目建议还是用中间件来限流,最好不要让这种请求进入项目中

 

代码编写

自定义一个注解类

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 #连接超时时间(毫秒)
application.yml

相关文章: