環境
1. 服務a,端口8080
2. 服務b,端口8020
3. 自定義@Idempotent註解
4. aop環繞通知
服務a
/**
* 服務a
*
* @param id 用戶id
* @param f 循環調用服務b次數
* @return
*/
@RequestMapping("/masterTest")
public String masterTest(Integer id, Integer f) {
// 生成token
String token = UUID.randomUUID().toString();
String body = null;
for (int a = 0; a < f; a++) {
// RPC調用slave-node服務
body = HttpRequest.get("http://127.0.0.1:8020/slave-node/rpc_b?id=" + id)
.header("idempotent-token", token)
.timeout(2000)
.execute().body();
Console.log(body);
}
return body;
}
服務b
@Idempotent()
@GetMapping("/rpc_b")
public String testB(Integer id) {
rpc = id;
Console.log("執行業務邏輯:{}", rpc);
return rpc + "";
}
自定義註解
package com.chuangqi.core.rpc;
import java.lang.annotation.*;
/**
* 在RPC被調用方方法上添加@Idempotent註解用於解決冪等性問題
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
public @interface Idempotent {
}
定義aop攔截
package com.chuangqi.core.rpc;
import cn.hutool.core.lang.Console;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import lombok.extern.log4j.Log4j2;
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.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.lang.reflect.Method;
import java.util.Arrays;
/**
* @Description 解決冪等性
* @Author qizhentao
* @Date 2020/3/10 16:23
* @Version 1.0
*/
@Component
@Aspect
@Log4j2
public class IdempotentAspect {
//@Around("@annotation(com.chuangqi.core.rpc.Idempotent)")
@Around("@annotation(Idempotent)")
public Object around(ProceedingJoinPoint jp) throws Throwable {
try {
//Class clazz = jp.getTarget().getClass();
//String methodName = jp.getSignature().getName();
Class[] parameterTypes = ((MethodSignature) jp.getSignature()).getMethod().getParameterTypes();
Method methdo = jp.getTarget().getClass().getMethod(jp.getSignature().getName(), parameterTypes);
if (methdo.getAnnotation(Idempotent.class) != null) {
// String value = methdo.getAnnotation(ApiOperation.class).value();
// 1.獲取request對象
HttpServletRequest request = ((ServletRequestAttributes) (RequestContextHolder.currentRequestAttributes())).getRequest();
// 獲取token
String token = request.getHeader("idempotent-token");
//Console.log("idempotent-token:{}", token);
// 2.獲取session或redis
HttpSession session = request.getSession();
// 3.判斷是否重複提交
String key = jp.getSignature() + "/" + Arrays.toString(jp.getArgs()); // 定義key名稱
//Console.log("方法key:{}", key);
if (session.getAttribute(key) == null || !session.getAttribute(key).equals(token)) {
session.setAttribute(key, token);
} else {
Console.error("重複提交:{}", key);
JSONObject obj = JSONUtil.createObj();
obj.put("code", "202");
obj.put("msg", "重複提交");
return obj.toString();
}
}
} catch (Exception ex) {
ex.toString();
}
// 正常執行目標業務
return jp.proceed();
}
}
測試
被調用方服務b控制檯打印: