实现思路:将请求的json数据,去除一些可变字段,将key升序排序,拼接成字符串并进行md5加密,再拼接一些用户信息,这样相同的请求参数得到的加密串必然一致,将此字符串作为key,存入redis,设置过期时间为1秒,一般重复提交都是在1000ms以内;
代码部分
package com.wang.learn.cloudredis.controller;
import com.alibaba.fastjson.JSONObject;
import com.wang.learn.cloudredis.entity.Book;
import com.wang.learn.cloudredis.utils.ReqDedupHelper;
import org.springframework.data.redis.connection.RedisStringCommands;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.types.Expiration;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import javax.validation.Valid;
/**
* ClassName RedisController
* Description
*
* @author wang
* Date 2021/6/19 10:48
*/
@RestController
@RequestMapping("/redis")
public class RedisController {
@Resource
private RedisTemplate<String, Object> stringRedisTemplate;
@RequestMapping("/repeat")
public Object repeat(@RequestBody @Valid Book book){
//用户
String userId= "12345678";
//接口名
String method = "pay";
//计算请求参数摘要,其中剔除里面请求时间的干扰
String dedupMD5 = ReqDedupHelper.dedupParamMD5(JSONObject.toJSONString(book),"time");
String KEY = "dedup:U=" + userId + "M=" + method + "P=" + dedupMD5;
// 1000毫秒过期,1000ms内的重复请求会认为重复
long expireTime = 1000;
long expireAt = System.currentTimeMillis() + expireTime;
String val = "expireAt@" + expireAt;
// 如果key不存在,set并返回true;如果key存在,不做操作返回false
Boolean firstSet = stringRedisTemplate.opsForValue().setIfAbsent(key, val, expireTime, TimeUnit.MILLISECONDS);
final boolean isConsiderDup;
if (firstSet != null && firstSet) {
return book;
} else {
return "订单重复!";
}
}
}
package com.wang.learn.cloudredis.utils;
import com.alibaba.fastjson.JSON;
import com.wang.learn.cloudredis.entity.Book;
import lombok.extern.slf4j.Slf4j;
import javax.xml.bind.DatatypeConverter;
import java.security.MessageDigest;
import java.util.Arrays;
import java.util.List;
import java.util.TreeMap;
/**
* ClassName Req
* Description
*
* @author wang
* Date 2021/6/19 10:29
*/
@Slf4j
public class ReqDedupHelper {
/**
*
* @param reqJSON 请求的参数,这里通常是JSON
* @param excludeKeys 请求参数里面要去除哪些字段再求摘要(如时间戳字段)
* @return 去除参数的MD5摘要
*/
public static String dedupParamMD5(final String reqJSON, String... excludeKeys) {
String decreptParam = reqJSON;
TreeMap paramTreeMap = JSON.parseObject(decreptParam, TreeMap.class);
if (excludeKeys!=null) {
List<String> dedupExcludeKeys = Arrays.asList(excludeKeys);
if (!dedupExcludeKeys.isEmpty()) {
for (String dedupExcludeKey : dedupExcludeKeys) {
if(paramTreeMap.containsKey(dedupExcludeKey)){
paramTreeMap.remove(dedupExcludeKey);
}
}
}
}
String paramTreeMapJSON = JSON.toJSONString(paramTreeMap);
String md5deDupParam = jdkMD5(paramTreeMapJSON);
log.debug("md5deDupParam = {}, excludeKeys = {} {}", md5deDupParam, Arrays.deepToString(excludeKeys), paramTreeMapJSON);
return md5deDupParam;
}
private static String jdkMD5(String src) {
String res = null;
try {
MessageDigest messageDigest = MessageDigest.getInstance("MD5");
byte[] mdBytes = messageDigest.digest(src.getBytes());
res = DatatypeConverter.printHexBinary(mdBytes);
} catch (Exception e) {
log.error("",e);
}
return res;
}
public static void main(String[] args) {
Book book = new Book();
book.setId(1);
book.setCount(2);
book.setName("java编程思想");
book.setTime(System.currentTimeMillis());
String key = dedupParamMD5(JSON.toJSONString(book), "time", "456");
System.out.println(key);
}
}
源码参考 https://gitee.com/wangLi1997/spring-cloud-learn cloud-redis模块