ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

请求防重处理

2022-06-21 23:31:39  阅读:87  来源: 互联网

标签:return 请求 处理 private key 防重 lockMillisecond public String


1、自定义注解

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public  @interface NoRepeatSubmit {

    /**
     * 默认1s钟以内算重复提交
     * @return
     */
    int lockTime() default 1000;
}

2、定义切面类

/**
 * 接口重复调用切面
 * 仅使用单机时
 */
@Aspect
@Component
@Slf4j
public class RepeatSubmitAspect {

     //定义切入点表达式
    @Pointcut("@annotation(noRepeatSubmit)")
    public void pointCut(NoRepeatSubmit noRepeatSubmit) {
    }

    @Around("pointCut(noRepeatSubmit)")
    public Object around(ProceedingJoinPoint pjp, NoRepeatSubmit noRepeatSubmit) throws Throwable {
        int lockMillisecond = noRepeatSubmit.lockTime();
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();
        Assert.notNull(request, "request can not null");
        //此处可以用或者JSessionId
        String token = request.getHeader("Cookie");
        String path = getPath(request);
        Object[] args = pjp.getArgs();
        String key = getKey(token, path, args);
        boolean isSuccess = LocalCache.getInstance().tryLock(key, lockMillisecond);
        log.info("获取锁 key = [{}], clientId = [{}]", key,token);

        if (isSuccess) {
            log.info("获取锁 成功, key = [{}], clientId = [{}]", key,token);
            // 获取锁成功
            Object result;
            try {
                // 执行进程
                result = pjp.proceed();
            } finally {
                // 解锁
                LocalCache.getInstance().releaseLock(key);
                log.info("释放锁 成功, key = [{}], clientId = [{}]", key,token);
            }
            return result;
        } else {
            // 获取锁失败,认为是重复提交的请求
            log.info("获取锁 失败, key = [{}]", key);
            LayUiResult result = new LayUiResult();
            result.setResultStr("操作太快了,请稍后重试");
            result.setResult(false);
            return result;
        }

    }

    private String getKey(String token, String path, Object... args) {
        return token + "_" + path + "_" + getParam(args);
    }

    public <T> String getNotNullFieldStr(T origin) {
        if (origin == null)
            return "";
        StringBuffer stringBuffer = new StringBuffer();
        Field[] fields = origin.getClass().getDeclaredFields();
        for (int i = 0; i < fields.length; i++) {
            try {
                fields[i].setAccessible(true);
                String name = fields[i].getName();
                Object value = fields[i].get(origin);
                if (null != value) {
                    stringBuffer.append(name).append("=").append(value).append("&");
                }
                fields[i].setAccessible(false);
            } catch (Exception e) {
            }
        }
        return stringBuffer.toString();
    }

    public static boolean isWrapClass(Class clz) {
        try {
            return ((Class) clz.getField("TYPE").get(null)).isPrimitive();
        } catch (Exception e) {
            return false;
        }
    }

    /**
     * 生成md5
     *
     * @param base
     * @return
     */
    public static String getMD5(String base) {
        String md5 = DigestUtils.md5DigestAsHex(base.getBytes());
        return md5;
    }

    private String getParam(Object[] args) {
        if (args == null) {
            return "";
        }
        StringBuffer stringBuffer = new StringBuffer();
        for (Object object : args) {
            if (object != null) {
                if (isWrapClass(object.getClass())) {
                    stringBuffer.append(object.toString());
                } else {
                    stringBuffer.append(getMD5(getNotNullFieldStr(object)));
                }
            }
        }
        return getMD5(stringBuffer.toString());
    }

    private String getPath(HttpServletRequest request) {
        String uri = request.getRequestURI();
        String contextPath = request.getContextPath();
        if (StringUtils.length(contextPath) > 0) {
            uri = StringUtils.substring(uri, contextPath.length());
        }
        return uri;
    }
}

3、本地接口调用内存缓存

package com.idea.aop;

import lombok.extern.slf4j.Slf4j;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;

/**
 * 本地接口调用内存缓存
 */
@Slf4j
public class LocalCache {
    private static LocalCache instance = new LocalCache();

    private LocalCache() {

    }

    public static LocalCache getInstance() {
        return instance;
    }

    /**
     * {key:{EXPIRED:时间戳,INSERT:时间戳},...}
     */
    private Map<String, Map<String, Object>> localMap = new HashMap<>();

    /**
     * 获取锁,当key存在且未过期时可以成功获得锁
     *
     * @param key
     * @param lockMillisecond
     * @return
     */
    public synchronized boolean tryLock(String key, long lockMillisecond) {
        //清理缓存
        clearExpired();
        //key已存在
        if (localMap.containsKey(key)) {
            //已过期
            if (expired(key, lockMillisecond)) {
                //已过期,从Map中移除
                localMap.remove(key);
                addCache(key, lockMillisecond);
                return true;
            } else {
                //未过期
                return false;
            }
        } else {
            //未在执行中
            addCache(key, lockMillisecond);
            return true;
        }
    }

    private void addCache(String key, long lockMillisecond) {
        Map map = new HashMap<>();
        map.put(EXPIRED, lockMillisecond);
        map.put(INSERT, new Date().getTime());
        localMap.put(key, map);
    }

    /**
     * 过期时间_KEY
     */
    /**
     * 创建时间_KEY
     */
    private static final String EXPIRED = "EXPIRED";
    private static final String INSERT = "INSERT";

    /**
     * 是否已过期
     *
     * @param key
     * @param lockMillisecond
     * @return
     */
    private synchronized boolean expired(Object key, long lockMillisecond) {
        Map<String, Object> map = localMap.get(key);
        //不存在 默认返回未过期
        if (map == null) {
            return true;
        }
        //数据接口调用时间戳
        long oldTime = (Long) map.get(INSERT);

        long now = new Date().getTime();

        //已过期:当前时间戳-(开始时间+过期时间)大于 0
        return (now - (oldTime + lockMillisecond)) > 0L;
    }

    /**
     * 清理Map中所有已过期的接口请求
     */
    private synchronized void clearExpired() {
        for (Map.Entry entry : localMap.entrySet()) {
            Object key = entry.getKey();
            Map<String, Object> map = localMap.get(key);
            long expired = (Long) map.get(EXPIRED);
            if (expired(key, expired)) {
                releaseLock(key);
            }
        }
    }

    /**
     * 释放锁
     *
     * @param key
     */
    public synchronized void releaseLock(Object key) {
        localMap.remove(key);
        log.info("本地缓存释放,Key:{}", key);
    }

}

 

标签:return,请求,处理,private,key,防重,lockMillisecond,public,String
来源: https://www.cnblogs.com/zouhong/p/16398806.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有