webservice接口报read time out_手写一个注解实现接口单位时间内的访问频率的拦截器...

目标

相信大家都听过接口安全,接口限流等这些词语,那么本篇文章就是从最基本的问题开始,带大家手写一个控制接口单位时间内访问频率的demo。好了,下面开始上代码。

环境+依赖

spring boot工程就不在此搭建了,小编直接贴出核心依赖

 org.springframework.boot  spring-boot-starter-data-redis  2.3.0.RELEASEorg.apache.commons  commons-pool2  2.4.2org.springframework.boot   spring-boot-starter-thymeleaforg.springframework.boot  spring-boot-starter-web

里面有用到redis,具体的配置可以参考小编之前的文章springboot整合redis,此处不再单独列出来了。

自定义注解 + 拦截器

  • 自定义注解
@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.METHOD)public @interface ApiLimit {    //time时间内请求的最大次数    int count();    //时间段内,单位:s    int time();}
  • 拦截器
public class ApiInterceptor implements HandlerInterceptor {    @Autowired    private RedisTemplate redisTemplate;    @Override    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {        if(handler instanceof HandlerMethod) {            HandlerMethod handlerMethod = (HandlerMethod) handler;            Method method = handlerMethod.getMethod();            ApiLimit apiLimit = method.getAnnotation(ApiLimit.class);            if(!ObjectUtils.isEmpty(apiLimit)) {                int count = apiLimit.count();                int time = apiLimit.time();                String ip = request.getRemoteAddr();                String requestURI = request.getRequestURI();                String redisKey = "apiKey_" + ip + "_" + requestURI;                System.out.println("redisKey======>" + redisKey);                if (ObjectUtils.isEmpty(redisTemplate.opsForValue().get(redisKey))) {                    AtomicInteger atomicInteger = new AtomicInteger(1);                    redisTemplate.opsForValue().set(redisKey, atomicInteger, time, TimeUnit.SECONDS);                    System.out.println("当前访问次数:" + ((AtomicInteger) redisTemplate.opsForValue().get(redisKey)).get());                } else {                    AtomicInteger atomicInteger = (AtomicInteger) redisTemplate.opsForValue().get(redisKey);                    if (atomicInteger.incrementAndGet() > count) {                        throw new RuntimeException("访问太频繁了,请休息一会");                    }                    Long expire = redisTemplate.getExpire(redisKey, TimeUnit.SECONDS);                    redisTemplate.opsForValue().set(redisKey, atomicInteger, expire ,TimeUnit.SECONDS);                    System.out.println("当前访问次数:" + ((AtomicInteger) redisTemplate.opsForValue().get(redisKey)).get());                }            } else {                return true;            }        }        return true;    }}

ip的获取大家可以借鉴网上的工具,此处为测试就简写了。

  • 拦截器配置

拦截器写好之后,需要将它注册到spring中

@Configurationpublic class WebMvcConfig implements WebMvcConfigurer {    @Bean    public ApiInterceptor apiInterceptor() {        return new ApiInterceptor();    }    @Override    public void addInterceptors(InterceptorRegistry registry) {      //这里我们添加了一个测试的路径        registry.addInterceptor(apiInterceptor()).addPathPatterns("/test/limit");    }}

测试类

只需要在需要拦截的接口上加上@ApiLimit注解,定义好时间和访问次数即可。

@Controller@RequestMapping("/test")public class TestController {    @GetMapping("/limit")    @ResponseBody    @ApiLimit(count = 5, time = 10)    public String get() {        return "hello world !!!";    }}

测试

我们先连续访问这个url,10s后再次访问。结果如下

f666a8d4e844a5887693886b9ac738ad.png

连续访问

5d0e0c5675f3544a32ebf96e9285a1a1.png

10s后再次访问

从测试结果可以看到,当我们10s连续访问这个接口超过5的话,直接抛出异常了。等10s后,再次访问,接口恢复如初。基本上满足了我们的小目标。

结尾

以上就是小编今天分享的内容了,喜欢小编的小伙伴可以关注小编。下篇文章小编将带领大家用spring boot整合开源项目来重新写一个接口限流的demo。好了,转发+评论+点赞 这篇文章,私信小编【接口限流】可获得源码。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值