有关于幂等操作--redis实现

本文介绍了一种基于注解和拦截器的幂等性检查方案,通过在方法上添加注解,利用拦截器检查请求参数,防止重复提交,确保系统的稳定性和数据一致性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.写一个标注

2,在需要幂等的方法上加上这个标注

3.用拦截器,拦截每一个请求,从请求中获取参数,如果带有标注则进行幂等检查,如果没有则不作

4.具体幂等检查(根据请求中的token和url来作为key,检查redis中是否存在

然后设置该key在redis中有效期,比如2秒)

代码如下

标注:

@Inherited
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface SameUrlData {

}
@Component
public class SameUrlDataInterceptor extends HandlerInterceptorAdapter {
	@Override
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
			throws Exception {
		if (handler instanceof HandlerMethod) {
			HandlerMethod handlerMethod = (HandlerMethod) handler;
			Method method = handlerMethod.getMethod();
			SameUrlData annotation = method.getAnnotation(SameUrlData.class);
			if (annotation != null) {
				if (repeatDataValidator(request)) {
					// 请求数据相同
					JSONObject result = new JSONObject();
					result.put("statusCode", "500");
					result.put("message", "Do not submit repeatedly");
					response.setCharacterEncoding("UTF-8");
					response.setContentType("application/json; charset=utf-8");
					response.getWriter().write(result.toString());
					response.getWriter().close();
					return false;
				}
			}
		}
		return true;
	}

	/**
	 * 验证同一个url数据是否相同提交,相同返回true
	 * 
	 * @param httpServletRequest
	 * @return
	 */
	public boolean repeatDataValidator(HttpServletRequest httpServletRequest) {
		// 获取请求参数map
		Map<String, String[]> parameterMap = httpServletRequest.getParameterMap();
		Iterator<Map.Entry<String, String[]>> it = parameterMap.entrySet().iterator();

		Map<String, String[]> parameterMapNew = new HashMap<>();
		while (it.hasNext()) {
			Map.Entry<String, String[]> entry = it.next();
			parameterMapNew.put(entry.getKey(), entry.getValue());
		}

		String token = httpServletRequest.getHeader("Auth-token");

		if (StringUtil.isBlank(token)) {
			// 如果没有token,直接放行
			return false;
		}
		// 过滤过后的请求内容
		String params = JSONObject.toJSONString(parameterMapNew);

		System.out.println("params===========" + params);

		String url = httpServletRequest.getRequestURI();
		Map<String, String> map = new HashMap<>();
		// key为接口,value为参数
		map.put(url, params);
		String nowUrlParams = map.toString();

		StringRedisTemplate smsRedisTemplate = SpringContextHolder.getBean(StringRedisTemplate.class);
		String redisKey = token + url;
		String preUrlParams = smsRedisTemplate.opsForValue().get(redisKey);
		if (preUrlParams == null) {// 如果上一个数据为null,表示还没有访问页面

			// 存放并且设置有效期,2秒
			smsRedisTemplate.opsForValue().set(redisKey, nowUrlParams, 2, TimeUnit.SECONDS);
			return false;

		} else {// 否则,已经访问过页面

			if (preUrlParams.equals(nowUrlParams)) {
				// 如果上次url+数据和本次url+数据相同,则表示重复添加数据
				return true;
			} else {// 如果上次 url+数据 和本次url加数据不同,则不是重复提交
				smsRedisTemplate.opsForValue().set(redisKey, nowUrlParams, 1, TimeUnit.SECONDS);
				return false;
			}

		}
	}

 

### Redis 等性解决方案 在分布式系统中,等性是一个重要的概念,它确保多次执行相同的操作不会对系统状态产生不同的影响。Redis 是一种高性能的内存数据库,常用于实现等性检查。以下是几种常见的 Redis 等性解决方案: #### 1. 使用唯一标识符(Token)存储和校验 通过生成一个具有唯一性的字符串(如 UUID),将其作为等 token 存入 Redis 中。当客户端发起请求时,首先检查该 token 是否已存在于 Redis 中。如果存在,则认为该请求已被处理,直接返回结果;如果不存在,则将 token 存入 Redis 并继续处理业务逻辑[^3]。 ```python import uuid import redis # 初始化 Redis 客户端 r = redis.StrictRedis(host='localhost', port=6379, db=0) def process_request(idempotent_token, data): if r.exists(idempotent_token): return {"message": "Request already processed", "status": "ignored"} # 将 token 存入 Redis,并设置过期时间以避免长期占用内存 r.setex(idempotent_token, 3600, "true") # 处理业务逻辑 result = handle_business_logic(data) return result ``` #### 2. 基于 Lua 脚本的原子操作 Lua 脚本可以在 Redis实现原子操作,从而保证等性。通过编写 Lua 脚本,可以一次性完成 token 的校验和插入操作,避免因网络延迟或并发问题导致的重复处理[^2]。 ```lua -- Lua 脚本示例 local token = KEYS[1] if redis.call("GET", token) then return "ignored" else redis.call("SET", token, "true", "EX", 3600) return "processed" end ``` ```python def process_request_with_lua(idempotent_token, data): lua_script = """ local token = KEYS[1] if redis.call("GET", token) then return "ignored" else redis.call("SET", token, "true", "EX", 3600) return "processed" end """ result = r.eval(lua_script, 1, idempotent_token) if result == b"processed": return handle_business_logic(data) else: return {"message": "Request already processed", "status": "ignored"} ``` #### 3. 分布式锁机制 在某些复杂场景下,可以使用 Redis 实现分布式锁来保证等性。例如,通过 Redission 或者自定义 Lua 脚本实现操作,确保同一时刻只有一个请求能够修改数据[^4]。 ```python from redis.lock import Lock def process_request_with_lock(r, lock_key, data): with Lock(r, lock_key, timeout=10): if r.exists(lock_key): return {"message": "Request already processed", "status": "ignored"} # 设置锁标志位 r.setex(lock_key, 3600, "true") # 处理业务逻辑 result = handle_business_logic(data) return result ``` #### 4. 数据库层面的唯一约束 除了 Redis,还可以结合数据库的唯一索引来实现等性。例如,在数据库表中为关键字段添唯一索引,确保重复的数据无法插入[^4]。 --- ###
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值