java接口的重复提交_接口重复提交解决方案

/**

* 防止重复提交的注解

*/

@Retention(RetentionPolicy.RUNTIME)

@Target({ElementType.METHOD})

public @interface AvoidRepeatSubmit {

long lockTime() default 1000;

}

import com.mushi.anno.AvoidRepeatSubmit;

import com.mushi.config.ResultGenerator;

import com.mushi.redis.service.RedisDistributedLock;

import lombok.extern.slf4j.Slf4j;

import org.aspectj.lang.ProceedingJoinPoint;

import org.aspectj.lang.annotation.Around;

import org.aspectj.lang.annotation.Aspect;

import org.aspectj.lang.annotation.Pointcut;

import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.stereotype.Component;

import org.springframework.util.Assert;

import org.springframework.web.context.request.RequestContextHolder;

import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;

import java.util.UUID;

/**

* 防止重复提交的切面

*/

@Aspect

@Component

@Slf4j

public class RepeatSubmitAspect {

@Autowired

private RedisDistributedLock redisDistributedLock;

/**

* 切点

*

* @param avoidRepeatSubmit

*/

@Pointcut("@annotation(avoidRepeatSubmit)")

public void pointCut(AvoidRepeatSubmit avoidRepeatSubmit) {

}

/**

* 利用环绕通知进行处理重复提交问题

*

* @param pjp

* @param avoidRepeatSubmit

* @return

* @throws Throwable

*/

@Around("pointCut(avoidRepeatSubmit)")

public Object around(ProceedingJoinPoint pjp, AvoidRepeatSubmit avoidRepeatSubmit) throws Throwable {

/**

* 获取锁的时间

*/

long lockSeconds = avoidRepeatSubmit.lockTime();

//获得request对象

HttpServletRequest request = httpServletRequest();

Assert.notNull(request, "request can not null");

// 此处可以用token或者JSessionId

String token = request.getHeader("token");

String path = request.getServletPath();

String key = getKey(token, path);

String clientId = getClientId();

//锁定多少秒

boolean isSuccess = redisDistributedLock.setLock(key, clientId, lockSeconds);

if (isSuccess) {

log.info("tryLock success, key = [{}], clientId = [{}]", key, clientId);

// 获取锁成功, 执行进程

Object result;

try {

result = pjp.proceed();

} finally {

//解锁

redisDistributedLock.releaseLock(key, clientId);

log.info("releaseLock success, key = [{}], clientId = [{}]", key, clientId);

}

return result;

} else {

// 获取锁失败,认为是重复提交的请求

log.info("tryLock fail, key = [{}]", key);

return ResultGenerator.genRepeatSubmitResult("重复请求,请稍后再试");

}

}

/**

* 获得request对象

*

* @return

*/

private HttpServletRequest httpServletRequest() {

ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();

return requestAttributes.getRequest();

}

/**

* 获得请求key

*

* @param token

* @param path

* @return

*/

private String getKey(String token, String path) {

return token + path;

}

/**

* 获得uuid

*

* @return

*/

private String getClientId() {

return UUID.randomUUID().toString();

}

}

import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

import org.springframework.data.redis.core.RedisCallback;

import org.springframework.data.redis.core.RedisTemplate;

import org.springframework.stereotype.Component;

import org.springframework.util.StringUtils;

import redis.clients.jedis.Jedis;

import redis.clients.jedis.JedisCluster;

import redis.clients.jedis.JedisCommands;

import javax.annotation.Resource;

import java.util.ArrayList;

import java.util.List;

@Component

public class RedisDistributedLock {

@Resource

private RedisTemplate redisTemplate;

public static final String UNLOCK_LUA;

static {

StringBuilder sb = new StringBuilder();

sb.append("if redis.call(\"get\",KEYS[1]) == ARGV[1] ");

sb.append("then ");

sb.append(" return redis.call(\"del\",KEYS[1]) ");

sb.append("else ");

sb.append(" return 0 ");

sb.append("end ");

UNLOCK_LUA = sb.toString();

}

private final Logger logger = LoggerFactory.getLogger(RedisDistributedLock.class);

public boolean setLock(String key, String clientId, long expire) {

try {

RedisCallback callback = (connection) -> {

JedisCommands commands = (JedisCommands) connection.getNativeConnection();

return commands.set(key, clientId, "NX", "PX", expire);

};

String result = redisTemplate.execute(callback);

return !StringUtils.isEmpty(result);

} catch (Exception e) {

logger.error("set redis occured an exception", e);

}

return false;

}

public String get(String key) {

try {

RedisCallback callback = (connection) -> {

JedisCommands commands = (JedisCommands) connection.getNativeConnection();

return commands.get(key);

};

String result = redisTemplate.execute(callback);

return result;

} catch (Exception e) {

logger.error("get redis occured an exception", e);

}

return "";

}

public boolean releaseLock(String key, String requestId) {

// 释放锁的时候,有可能因为持锁之后方法执行时间大于锁的有效期,此时有可能已经被另外一个线程持有锁,所以不能直接删除

try {

List keys = new ArrayList<>();

keys.add(key);

List args = new ArrayList<>();

args.add(requestId);

// 使用lua脚本删除redis中匹配value的key,可以避免由于方法执行时间过长而redis锁自动过期失效的时候误删其他线程的锁

// spring自带的执行脚本方法中,集群模式直接抛出不支持执行脚本的异常,所以只能拿到原redis的connection来执行脚本

RedisCallback callback = (connection) -> {

Object nativeConnection = connection.getNativeConnection();

// 集群模式和单机模式虽然执行脚本的方法一样,但是没有共同的接口,所以只能分开执行

// 集群模式

if (nativeConnection instanceof JedisCluster) {

return (Long) ((JedisCluster) nativeConnection).eval(UNLOCK_LUA, keys, args);

}

// 单机模式

else if (nativeConnection instanceof Jedis) {

return (Long) ((Jedis) nativeConnection).eval(UNLOCK_LUA, keys, args);

}

return 0L;

};

Long result = redisTemplate.execute(callback);

return result != null && result > 0;

} catch (Exception e) {

logger.error("release lock occured an exception", e);

} finally {

// 清除掉ThreadLocal中的数据,避免内存溢出

//lockFlag.remove();

}

return false;

}

}

然后再需要接口防重的接口上加上AvoidRepeatSubmit注解

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值