Java使用Interceptor+redis去实现请求重复频繁提交问题
1. 前言:
在开发阶段中发现app时常会出现这样的一种问题:如果对一次请求在很短的时间多次重复提交,则会引起一系列的问题,对此,本人自己琢磨后写成博客,希望能对大家有所帮助!
2. 准备阶段
1.java开发环境
2.redis
3.jar包依赖
<dependency><groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.1.0</version>
</dependency>
4.redis安装就不用讲了
这个比较详细 http://www.runoob.com/redis/redis-install.html
3. 实例
RedisUtils
public class RedisUtils {
private static Logger logger = LoggerFactory.getLogger(RedisUtils.class);
public final static String VIRTUAL_COURSE_PREX = "vc_";
private Jedis jedis;//非切片额客户端连接
private static JedisPool jedisPool;//非切片连接池
private ShardedJedis shardedJedis;//切片额客户端连接
private ShardedJedisPool shardedJedisPool;//切片连接池
public RedisUtils()
{
initialPool();
initialShardedPool();
shardedJedis = shardedJedisPool.getResource();
jedis = jedisPool.getResource();
}
/**
* 初始化非切片池
*/
private static void initialPool()
{
// 池基本配置
JedisPoolConfig config = new JedisPoolConfig();
config.setMaxIdle(20);
config.setMaxIdle(5);
config.setMaxWait(1000l);
config.setTestOnBorrow(false);
jedisPool = new JedisPool(config,"127.0.0.1",6379);
}
/**
* 初始化切片池
*/
private void initialShardedPool()
{
// 池基本配置
JedisPoolConfig config = new JedisPoolConfig();
config.setMaxIdle(20);
config.setMaxIdle(5);
config.setMaxWait(1000l);
config.setTestOnBorrow(false);
// slave链接
List<JedisShardInfo> shards = new ArrayList<JedisShardInfo>();
shards.add(new JedisShardInfo("127.0.0.1", 6379));
// 构造池
shardedJedisPool = new ShardedJedisPool(config, shards);
}
public void show() {
KeyOperate();
StringOperate();
ListOperate();
SetOperate();
SortedSetOperate();
HashOperate();
jedisPool.returnResource(jedis);
shardedJedisPool.returnResource(shardedJedis);
}
/**
* 在多线程环境同步初始化
*/
private synchronized static void poolInit() {
if (jedisPool == null) {
initialPool();
}
}
/**
* 同步获取Jedis实例
* @return Jedis
*/
public synchronized static Jedis getJedis() {
if (jedisPool == null) {
poolInit();
}
Jedis jedis = null;
try {
if (jedisPool != null) {
jedis = jedisPool.getResource();
jedis.auth("xxxxxxx"); //设置密码
// jedis.auth(redisCacheConfig.getAuth());
}
} catch (Exception e) {
logger.error("Get jedis error : "+e);
e.printStackTrace();
}finally{
returnResource(jedis);
}
return jedis;
}
/**
* 释放jedis资源
* @param jedis
*/
public static void returnResource(final Jedis jedis) {
if (jedis != null && jedisPool !=null) {
jedisPool.returnResource(jedis);
}
}
/**
* 得到Key
* @param key
* @return
*/
public static String buildKey(String key){
return key;
}
/**
* 设置 String
* @param key
* @param value
*/
public static void setString(String key ,String value){
try {
value = StringUtils.isNotEmpty(value) ? "" : value;
getJedis().set(buildKey(key),value);
} catch (Exception e) {
logger.error("Set keyex error : "+e);
}
}
/**
* 设置 过期时间
* @param key
* @param seconds 以秒为单位
* @param value
*/
public static void setString(String key ,int seconds,String value){
try {
value = StringUtils.isNotEmpty(value) ? "" : value;
getJedis().setex(buildKey(key), seconds, value);
} catch (Exception e) {
logger.error("Set keyex error : "+e);
}
}
/**
* 获取String值
* @param key
* @return value
*/
public static String getString(String key){
String bKey = buildKey(key);
if(getJedis() == null || !getJedis().exists(bKey)){
return null;
}
return getJedis().get(bKey);
}
/**
* 设置 list
* @param <T>
* @param key
* @param value
*/
public static <T> void setList(String key ,List<T> list){
try {
getJedis().set(key.getBytes(),ObjectTranscoder.serialize(list));
} catch (Exception e) {
logger.error("Set key error : "+e);
}
}
/**
* 获取list
* @param <T>
* @param key
* @return list
*/
public static <T> List<T> getList(String key){
String bKey = buildKey(key);
if(getJedis() == null || !getJedis().exists(key.getBytes())){
return null;
}
byte[] in = getJedis().get(key.getBytes());
List<T> list = (List<T>) ObjectTranscoder.deserialize(in);
return list;
}
/**
* 设置 map
* @param <T>
* @param key
* @param value
*/
public static <T> void setMap(String key ,Map<String,T> map){
try {
getJedis().set(key.getBytes(),ObjectTranscoder.serialize(map));
} catch (Exception e) {
logger.warn("Set key error : "+e);
}
}
/**
* 获取list
* @param <T>
* @param key
* @return list
*/
public static <T> Map<String,T> getMap(String key){
String bKey = buildKey(key);
if(getJedis() == null || !getJedis().exists(key.getBytes())){
return null;
}
byte[] in = getJedis().get(key.getBytes());
Map<String,T> map = (Map<String, T>) ObjectTranscoder.deserialize(in);
return map;
}
public static void main(String[] args) {
RedisUtils redisCli=new RedisUtils();
//redisCli.KeyOperate();
redisCli.SetOperate();
}
}
配置拦截器
public class TokenInterceptor extends HandlerInterceptorAdapter {
//日志文件
public Log logger = LogFactory.getLog(this.getClass());
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
Long value = System.currentTimeMillis(); //获取当前时间的毫秒数
String nowRequestName = request.getServletPath().substring(1,request.getServletPath().length());//获取当前请求的名称view
String umerId = request.getParameter("umerId"); //获取点击的人
String key = null; //组合Key
String reidsValue = null; //根据key去取值
String userType = null; //用户类型
if(StringUtils.isEmpty(umerId)){
//umerId 为空
String userId = request.getParameter("userId");
if(StringUtils.isEmpty(userId)){
//如果userId为空
String openId = request.getParameter("openId");
if(StringUtils.isEmpty(openId)){
//如果openId也为空
//则不拦截
return super.preHandle(request, response, handler);
}else {
//openId
userType = "openId";
umerId = openId;
key = openId + nowRequestName; //组合Key
}
}else {
//userId不为空
userType = "userId";
umerId = userId;
key = userId + nowRequestName; //组合Key
}
}else {
//umerId不为空
userType = "umerId";
key = umerId + nowRequestName; //组合Key
}
//获取值
reidsValue = RedisUtils.getJedis().get(key); //根据key去取值
if(StringUtils.isEmpty(reidsValue)){
//说明为空
RedisUtils.getJedis().set(key,String.valueOf(value)); //设置值进去,并且过期时间为2秒
RedisUtils.getJedis().expire(key, 2);
}else{
//不为空
//开始判断
Long time = Long.parseLong(reidsValue);
if(value - time > 2000){
//如果相隔大于2秒
RedisUtils.getJedis().set(key,String.valueOf(value)); //设置值进去,并且过期时间为2秒
RedisUtils.getJedis().expire(key,2);
}else{
//如果相隔小于2秒
RedisUtils.getJedis().set(key,String.valueOf(value)); //设置值进去,并且过期时间为2秒
RedisUtils.getJedis().expire(key, 2);
PrintWriter out = response.getWriter();
out.print("interceptor | TokenInterceptor | " + userType + ":" + umerId + " | error : 请求间隔低于2s,频率过快!");
//logger.error("interceptor | TokenInterceptor | umerId :" + userName + " | error : 请求间隔低于2s,频率过快!");
return false;
}
}
return super.preHandle(request, response, handler);
}
}
最后加载拦截器【spring-boot】
@Configuration //标注此文件为一个配置项,spring boot才会扫描到该配置。该注解类似于之前使用xml进行配置
public class ServletContextConfig extends WebMvcConfigurerAdapter {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new TokenInterceptor()).addPathPatterns("/**"); //对来自/** 这个链接来的请求进行拦截
}
}