大家好,欢迎来到IT知识分享网。
1.简述
- 目的:一定时间内,同样的请求(业务参数相同)访问同一个接口,则只能成功一次,其余被拒绝。
2.引入redis支持
因为需要通过redis
的setnx
确保只有一个接口能够正常访问,所以需要引入redis。
2.1.pom.xml
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<exclusions>
<!-- 需要排除哪些包由具体项目觉得 -->
<exclusion>
<artifactId>spring-boot-starter-logging</artifactId>
<groupId>org.springframework.boot</groupId>
</exclusion>
</exclusions>
</dependency>
IT知识分享网
2.2.application.properties
IT知识分享网spring.redis.host=11.22.33.44
spring.redis.port=26379
spring.redis.database=1
spring.redis.pool.max-active=8
spring.redis.pool.max-wait=-1
spring.redis.pool.max-idle=500
spring.redis.pool.min-idle=0
spring.redis.timeout=0
2.3.Redis JUnit Test Case
/** * @author hanchao */
@RunWith(SpringRunner.class)
@SpringBootTest
public class RedisTemplateTest {
@Resource
private RedisTemplate<String,String > redisTemplate;
@Test
public void simpleTest() {
ValueOperations<String,String> valueOperations = redisTemplate.opsForValue();
String key = "RedisTemplateTest-simpleTest-001";
valueOperations.set(key,key+key);
System.out.println(valueOperations.get(key));
}
}
3.引入幂等
3.1.幂等异常
IT知识分享网/** * 用于专门处理幂等相关异常。 * @author hanchao */
public class IdempotentException extends RuntimeException {
public IdempotentException(String message) {
super(message);
}
@Override
public String getMessage() {
return super.getMessage();
}
}
3.2.幂等注解
/** * 幂等注解 * @author wangchao */
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Idempotent {
/** * 幂等名称,作为redis缓存Key的一部分。 */
String value();
/** * 幂等过期时间,即:在此时间段内,对API进行幂等处理。 */
long expireMillis();
}
3.3.幂等切面
/** * 幂等切面 * @author wangchao */
@Aspect
@Component
@ConditionalOnClass(RedisTemplate.class)
public class IdempotentAspect {
private static final Logger LOGGER = LoggerFactory.getLogger(IdempotentAspect.class);
/** * redis缓存key的模板 */
private static final String KEY_TEMPLATE = "idempotent_%s";
@Resource
private RedisTemplate<String,String> redisTemplate;
/** * 根据实际路径进行调整 */
@Pointcut("@annotation(pers.hanchao......anno.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前缀 + @Idempotent.value() + 方法签名 + 参数 构建缓存键值
//确保幂等处理的操作对象是:同样的 @Idempotent.value() + 方法签名 + 参数
String key = String.format(KEY_TEMPLATE, idempotent.value() + "_" + KeyUtil.generate(method, joinPoint.getArgs()));
//通过setnx确保只有一个接口能够正常访问
//调用KeyUtil工具类生成key
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 {
LOGGER.debug("Idempotent hits, key=" + key);
throw new IdempotentException("Idempotent hits, key=" + key);
}
}
}
3.4.工具类
/** * Key生成工具 * @author hanchao */
public class KeyUtil {
private static final Logger LOGGER = LoggerFactory.getLogger(KeyUtil.class);
/** * 根据{方法名 + 参数列表}和md5转换生成key */
public static String generate(Method method, Object... args) {
StringBuilder sb = new StringBuilder(method.toString());
for (Object arg : args) {
sb.append(toString(arg));
}
return DigestUtils.md5Hex(sb.toString());
}
private static String toString(Object object) {
if (object == null) {
return "null";
}
if (object instanceof Number) {
return object.toString();
}
//调用json工具类转换成String
return JsonUtil.toJson(object);
}
}
/** * Json格式化工具 * @author hanchao */
public class JsonUtil {
private static final Logger LOGGER = LoggerFactory.getLogger(JsonUtil.class);
private static final ObjectMapper MAPPER = new ObjectMapper();
static {
MAPPER.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false).setSerializationInclusion(Include.NON_NULL);
}
/** * Java Object Maps To Json */
public static String toJson(Object obj) {
String result;
if (obj == null || obj instanceof String) {
return (String) obj;
}
try {
result = MAPPER.writeValueAsString(obj);
} catch (Exception e) {
LOGGER.error("Java Object Maps To Json Error !");
throw new RuntimeException("Java Object Maps To Json Error !", e);
}
return result;
}
}
4.对接口标记幂等注解
@RestController
public class DemoController {
@Resource
private DemoService demoService;
/** * @Idempotent的value值随意,一般保持与接口url一致接口。 */
@Idempotent(value = "/cock/alarm", expireMillis = 1000L)
@PostMapping(value = "/cock/alarm")
public String demo(@RequestBody DemoPo po) {
//..
}
}
免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://yundeesoft.com/7588.html