当前位置: 代码网 > it编程>编程语言>Java > Spring Boot通过Redis实现防止重复提交

Spring Boot通过Redis实现防止重复提交

2024年07月02日 Java 我要评论
一、什么是幂等。1、什么是幂等:相同的条极下,执行多次结果拿到的结果都是一样的。举个例子比如相同的参数情况,执行多次,返回的数据都是样的。那什么情况下要考虑幂等,重复新增数据。2、解决方案:解决的方式

一、什么是幂等。

1、什么是幂等:相同的条极下,执行多次结果拿到的结果都是一样的。举个例子比如相同的参数情况,执行多次,返回的数据都是样的。那什么情况下要考虑幂等,重复新增数据。

2、解决方案:解决的方式有很多种,本文使用的是本地锁。

二、实现思路

1、首先我们要确定多少时间内,相同请求参数视为一次请求,比如2秒内相同参数,无论请求多少次都视为一次请求。

2、一次请求的判断依据是什么,肯定是相同的形参,请求相同的接口,在1的时间范围内视为相同的请求。所以我们可以考虑通过参数生成一个唯一标识,这样我们根据唯一标识来判断。

三、代码实现

1、这里我选择spring的redis来实现,但如果有集群的情况,还是要用集群redis来处理。当第一次获取请求时,根据请求url、controller层调用的方法、请求参数生成md5值这三个条件作为依据,将其作为redis的key和value值,并设置失效时间,当在失效时间之内再次请求时,根据是否已存在key值来判断接收还是拒绝请求来实现拦截。

2、pom.xml依赖

<dependency>
	<groupid>org.springframework.boot</groupid>
	<artifactid>spring-boot-starter-data-redis</artifactid>
	<exclusions>
		<exclusion>
			<groupid>io.lettuce</groupid>
			<artifactid>lettuce-core</artifactid>
		</exclusion>
	</exclusions>
	<version>2.0.6.release</version>
</dependency>
<dependency>
	<groupid>org.aspectj</groupid>
	<artifactid>aspectjweaver</artifactid>
	<version>1.8.9</version>
</dependency>
<dependency>
	<groupid>redis.clients</groupid>
	<artifactid>jedis</artifactid>
	<version>2.9.1</version>
</dependency>
<dependency>
	<groupid>org.apache.commons</groupid>
	<artifactid>commons-pool2</artifactid>
	<version>2.6.0</version>
</dependency>
<dependency>
	<groupid>com.alibaba</groupid>
	<artifactid>fastjson</artifactid>
	<version>1.2.31</version>
</dependency>

3、在application.yml中配置redis

spring:
  redis:
    database: 0
    host: xxx
    port: 6379
    password: xxx
    timeout: 1000
    pool:
      max-active: 1
      max-wait: 10
      max-idle: 2
      min-idle: 50

4、自定义异常类idempotentexception

package com.demo.controller;
public class idempotentexception extends runtimeexception {
	public idempotentexception(string message) {
		super(message);
	}
	
	@override
	public string getmessage() {
		return super.getmessage();
	}
}

5、自定义注解

package com.demo.controller;
 
import java.lang.annotation.documented;
import java.lang.annotation.elementtype;
import java.lang.annotation.retention;
import java.lang.annotation.retentionpolicy;
import java.lang.annotation.target;
 
@target(elementtype.method)
@retention(retentionpolicy.runtime)
@documented
public @interface idempotent {
	// redis的key值一部分
	string value();
	// 过期时间
	long expiremillis();
}

6、自定义切面

package com.demo.controller;
import java.lang.reflect.method;
import java.util.objects;
import javax.annotation.resource;
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.aspectj.lang.reflect.methodsignature;
import org.springframework.boot.autoconfigure.condition.conditionalonclass;
import org.springframework.data.redis.core.rediscallback;
import org.springframework.data.redis.core.redistemplate;
import org.springframework.stereotype.component;
import redis.clients.jedis.jediscommands;
 
@component
@aspect
@conditionalonclass(redistemplate.class)
public class idempotentaspect {
	private static final string key_template = "idempotent_%s";
 
	@resource
	private redistemplate<string, string> redistemplate;
 
	//填写扫描加了自定义注解的类所在的包。
	@pointcut("@annotation(com.demo.controller.idempotent)")
	public void executeidempotent() {
 
	}
 
	@around("executeidempotent()")  //环绕通知
	public object around(proceedingjoinpoint joinpoint) throws throwable {
		method method = ((methodsignature) joinpoint.getsignature()).getmethod();
		idempotent idempotent = method.getannotation(idempotent.class);
        //注意key的生成规则		
string key = string.format(key_template,
				idempotent.value() + "_" + keyutil.generate(method, joinpoint.getargs()));
		string redisres = redistemplate
				.execute((rediscallback<string>) conn -> ((jediscommands) conn.getnativeconnection()).set(key, key,
						"nx", "px", idempotent.expiremillis()));
		if (objects.equals("ok", redisres)) {
			return joinpoint.proceed();
		} else {
			throw new idempotentexception("idempotent hits, key=" + key);
		}
	}
}

//注意这里不需要释放锁操作,因为如果释放了,在限定时间内,请求还是会再进来,所以不能释放锁操作。

7、key生成工具类

package com.demo.controller;
 
import java.io.unsupportedencodingexception;
import java.lang.reflect.method;
import java.security.messagedigest;
import java.security.nosuchalgorithmexception;
 
import com.alibaba.fastjson.json;
 
public class keyutil {
	public static string generate(method method, object... args) throws unsupportedencodingexception {
		stringbuilder sb = new stringbuilder(method.tostring());
		for (object arg : args) {
			sb.append(tostring(arg));
		}
		return md5(sb.tostring());
	}
 
	private static string tostring(object object) {
		if (object == null) {
			return "null";
		}
		if (object instanceof number) {
			return object.tostring();
		}
		return json.tojsonstring(object);
	}
 
	public static string md5(string str) {
		stringbuilder buf = new stringbuilder();
		try {
			messagedigest md = messagedigest.getinstance("md5");
			md.update(str.getbytes());
			byte b[] = md.digest();
			int i;
			for (int offset = 0; offset < b.length; offset++) {
				i = b[offset];
				if (i < 0)
					i += 256;
				if (i < 16)
					buf.append(0);
				buf.append(integer.tohexstring(i));
			}
		} catch (nosuchalgorithmexception e) {
			e.printstacktrace();
		}
		return buf.tostring();
	}

8、在controller中标记注解

package com.demo.controller;
 
import javax.annotation.resource;
 
import org.springframework.data.redis.core.redistemplate;
import org.springframework.web.bind.annotation.postmapping;
import org.springframework.web.bind.annotation.requestbody;
import org.springframework.web.bind.annotation.restcontroller;
 
@restcontroller
public class democontroller {
	@resource
	private redistemplate<string, string> redistemplate;
	
	@postmapping("/redis")
	@idempotent(value = "/redis", expiremillis = 5000l)
	public string redis(@requestbody user user) {
		return "redis access ok:" + user.getusername() + " " + user.getuserage();
	}
}

到此这篇关于spring boot通过redis实现防止重复提交的文章就介绍到这了,更多相关springboot redis防止重复提交内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网! 

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2025  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com