目标
相信大家都听过接口安全,接口限流等这些词语,那么本篇文章就是从最基本的问题开始,带大家手写一个控制接口单位时间内访问频率的demo。好了,下面开始上代码。
环境+依赖
spring boot工程就不在此搭建了,小编直接贴出核心依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<version>2.3.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
<version>2.4.2</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
里面有用到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中
@Configuration
public 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后再次访问。结果如下
连续访问
10s后再次访问
从测试结果可以看到,当我们10s连续访问这个接口超过5的话,直接抛出异常了。等10s后,再次访问,接口恢复如初。基本上满足了我们的小目标。
结尾
以上就是小编今天分享的内容了,喜欢小编的小伙伴可以关注小编。下篇文章小编将带领大家用spring boot整合开源项目来重新写一个接口限流的demo。好了,转发+评论+点赞 这篇文章,私信小编【接口限流】可获得源码。
本文来自盼夏投稿,不代表胡巴网立场,如若转载,请注明出处:https://www.hu85.com/180647.html
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 xxxxx@qq.com 举报,一经查实,本站将立刻删除。