对于重复提交的问题 主要涉及到时 幂等 问题,那么先说一下什么是幂等。
幂等:F(F(X)) = F(X)多次运算结果一致;简单点说就是对于完全相同的操作,操作一次与操作多次的结果是一样的。
在开发中,我们都会涉及到对数据库操作。例如:
select 查询天然幂等
delete 删除也是幂等,删除同一个多次效果一样
update 直接更新某个值(如:状态 字段固定值),幂等
update 更新累加操作(如:商品数量 字段),非幂等
(可以采用简单的乐观锁和悲观锁 个人更喜欢乐观锁。
乐观锁:数据库表加version字段的方式;
悲观锁:用了 select…for update 的方式,* 要使用悲观锁,我们必须关闭mysql数据库的自动提交属性。
这种在大数据量和高并发下效率依赖数据库硬件能力,可针对并发量不高的非核心业务;)
insert 非幂等操作,每次新增一条 重点 (数据库简单方案:可采取数据库唯一索引方式;这种在大数据量和高并发下效率依赖数据库硬件能力,可针对并发量不高的非核心业务;)
google cache 代码实现 注解方式 Single lock
pom.xml 引入
<dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <version>21.0</version> </dependency>
配置文件 .yml
resubmit: local: timeOut: 60
实现代码
import java.lang.annotation.*; @Target({ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited public @interface LocalLock { String key() default ""; }
import com.google.common.cache.Cache; import com.google.common.cache.CacheBuilder; import com.his.sale.sybgl.common.exception.ResultUtil; import com.hx.common.model.Result; import lombok.Data; import org.apache.commons.lang3.StringUtils; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.reflect.MethodSignature; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Configuration; import org.springframework.web.context.request.RequestAttributes; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes; import javax.servlet.http.HttpServletRequest; import java.lang.reflect.Method; import java.security.NoSuchAlgorithmException; import java.util.ArrayList; import java.util.List; import java.util.concurrent.TimeUnit; /** * @author: xx * @description: 单机放重复提交 */ @Data @Aspect @Configuration public class LocalLockMethodInterceptor { @Value("${spring.profiles.active}") private String springProfilesActive; @Value("${spring.application.name}") private String springApplicationName; private static String expireTimeSecond = "5"; @Value("${resubmit:local:timeOut}") public void setExpireTimeSecond(String expireTimeSecond) { LocalLockMethodInterceptor.expireTimeSecond = expireTimeSecond; } //定义缓存,设置最大缓存数及过期日期 private static final Cache<String, Object> CACHE = CacheBuilder.newBuilder().maximumSize(1000).expireAfterWrite(Long.parseLong(expireTimeSecond), TimeUnit.SECONDS).build(); @Around(value = "execution(public * *(..)) && @annotation(com.his.common.util.LocalLock)") public Object interceptor(ProceedingJoinPoint joinPoint) { MethodSignature signature = (MethodSignature) joinPoint.getSignature(); Method method = signature.getMethod(); // LocalLock localLock = method.getAnnotation(LocalLock.class); try { String key = getLockUniqueKey(signature, joinPoint.getArgs()); if (CACHE.getIfPresent(key) != null) { return ResultUtil.succeed(null,"500","不允许重复提交,请稍后再试"); } CACHE.put(key, key); return joinPoint.proceed(); } catch (Throwable throwable) { return ResultUtil.succeed(null,"500",throwable.getMessage()); // throw new RuntimeException(throwable.getMessage()); } finally { } } /** * 获取唯一标识key * * @param methodSignature * @param args * @return */ private String getLockUniqueKey(MethodSignature methodSignature, Object[] args) throws NoSuchAlgorithmException { //请求uri, 获取类名称,方法名称 RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes(); ServletRequestAttributes servletRequestAttributes = (ServletRequestAttributes) requestAttributes; HttpServletRequest request = servletRequestAttributes.getRequest(); // HttpServletResponse responese = servletRequestAttributes.getResponse(); // //获取用户信息 // String userMsg = SecurityUtils.getUsername(); //获取登录用户名称 // //1.判断用户是否登录 // if (StringUtils.isEmpty(userMsg)) { //未登录用户获取真实ip // userMsg = IpUtils.getIpAddr(request); // } String hash = ""; List list = new ArrayList(); if (args.length > 0) { String[] parameterNames = methodSignature.getParameterNames(); for (int i = 0; i < parameterNames.length; i++) { Object obj = args[i]; list.add(obj); } // hash = JSONObject.toJSONString(list); } //项目名称 + 环境编码 + 获取类名称 + 方法名称 + 唯一key String key = "locallock:" + springApplicationName + ":" + springProfilesActive + ":" + request.getRequestURI(); if (StringUtils.isNotEmpty(key)) { key = key + ":" + hash; } // key = MyMD5Util.getMD5(key); return key; } }
使用:
https://blog.csdn.net/qq_33454058/article/details/125516310