自定義註解解決RPC冪等性問題

環境

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控制檯打印:

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章