在网上看见有人问一个问题:想限制一下某个接口在一分钟之内只能被同一个ip请求指定次数。
方法比较多,这里就用Redis做一个简单的限制。
大致逻辑:
把请求的ip作为key,请求次数作为value存储在Redis里面,第一次请求value为1,以后每次请求加1,设置过期时间60s,
每次请求都重置过期时间,每次请求过来都需要判断value是否大于指定次数即可;
这里重置过期时间主要是为了让请求必须间隔一定时间才能再次请求,如果一直在请求,那么,到达一定次数后就一直请求不通,必须间隔一定时间,不设置也可以,每一分钟重新计算请求次数;
每次请求重置过期时间:有个脚本每秒都在调用该接口,就算调了一天,也只能调用指定一分钟内的调用次数;
不重置过期时间:有个脚本每秒都在调用该接口,每过一分钟,就可以成功调用三次;
springBoot + Redis实现,springMVC也是同理,主要是思路了;
1. pom文件添加依赖
<!-- Radis -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
2. 配置文件配置Redis
#redis的ip地址
redis.hostName=127.0.0.1
#端口号
redis.port=6379
#如果有密码
#redis.password=
#客户端超时时间单位是毫秒 默认是2000
redis.timeout=10000
3.实现代码
package com.example.servletDemo.controller;
import java.util.concurrent.TimeUnit;
import javax.servlet.http.HttpServletRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
@RequestMapping("/Demo")
public class DemoController {
@Autowired
StringRedisTemplate stringRedisTemplate;
/**
* key过期时间(秒)
*/
private final Long EXPIRATIONTIME_SECONDS = 60L;
/**
* key 前缀,防止和其他地方的key可以冲突
*/
private final String prefix = "Demo:";
/**
* 请求次数限制:一分钟内一个IP只能请求指定次数
* 1.检查IP是否存在(一分钟内是否登录过),如果不存在,设置key和value 过期时间1分钟,
* 存在,value+1并且设置过期时间,value大于指定次数,返回请求超过限制
*
* key为ip ,value 为调用次数
*
* @param request
* @return
*/
@ResponseBody
@RequestMapping("/getInfo")
public String login(HttpServletRequest request) {
// 获取请求IP
String ip = request.getHeader("x-forwarded-for");
if(ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip) || "null".equals(ip)){
ip = "" + request.getHeader("Proxy-Client-IP");
}
if(ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip) || "null".equals(ip)){
ip = "" + request.getHeader("WL-Proxy-Client-IP");
}
if(ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip) || "null".equals(ip)){
ip = "" + request.getRemoteAddr();
}
// 前缀+ip构成key
String key = prefix+ip;
//检查key是否存在,返回boolean值
boolean flag = stringRedisTemplate.hasKey(key);
if (flag) {
// 调用次数+1
stringRedisTemplate.boundValueOps(key).increment(1);
// 设置过期时间
stringRedisTemplate.expire(ip, EXPIRATIONTIME_SECONDS,TimeUnit.SECONDS);
String sumVal = stringRedisTemplate.opsForValue().get(key);
int sum = Integer.valueOf(sumVal);
if (sum > 3) {
System.out.println("第" + sum + "次请求,请求失败!");
return "一分钟内不能再次请求!";
}
}else {
// 第一次调用,所以value(调用次数)设置为1
stringRedisTemplate.opsForValue().set(key, "1",EXPIRATIONTIME_SECONDS,TimeUnit.SECONDS);
}
String num = stringRedisTemplate.opsForValue().get(key);
System.out.println("第"+num+"次请求,请求成功!");
return "请求成功";
}
}
效果: