IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 大数据 -> 防重复提交(注解+AOP) -> 正文阅读

[大数据]防重复提交(注解+AOP)

恶意请求,服务有瓶颈、复杂业务等!

让某个接口某个人(ip)在某段时间内只能请求N次。
在项目中比较常见的问题也有,那就是连点按钮导致请求多次。

全部由后端来控制,大致方案有使用拦截器、过滤器、切面。

某些场景幂等性。

大致思路:请求的时候,服务器通过redis 记录下你请求信息。
在redis 保存的key 是有时效性的,过期就会删除。

示例注解+AOP方式的防刷实现

?第一步:定义防刷注解、启动类开启切面支持、pom引入依赖

@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RequestRepeatAnnotation {

    //允许访问的次数,默认值MAX_VALUE
    int count() default Integer.MAX_VALUE;
 
    // 时间段,单位为毫秒,默认值一分钟
    long time() default 60000;
}

第二步:定义切面和防刷逻辑实现


/**
 * 请求重复拦截
 * 
 * @author Be.insighted
 *
 */
@Aspect
@Component
public class RequestRepeatAspect {

	private static final Logger logger = LoggerFactory.getLogger(RequestRepeatAspect.class);

	@Autowired
	private CacheManager cacheManager;

	// 接口超时时间为5s 可以配置在配置文件里
	private static final int DEFAULT_EXPIRE_TIME = 5;

	private static final String LOCK_TITLE = "biz_Lock_";

	@Pointcut("@annotation(cn.ccccltd.smp.interceptor.ann.RequestRepeatAnnotation)")
	private void requestRepeatCheck() {

	}

	@Around(value = "requestRepeatCheck()")
	public Object excute(ProceedingJoinPoint pjp) throws Throwable {

		Signature signature = pjp.getSignature();
		// String className = pjp.getTarget().getClass().getName();
		String method = signature.getName();

		MethodSignature methodSignature = (MethodSignature) signature;
		Method targetMethod = methodSignature.getMethod();
		if (targetMethod.isAnnotationPresent(RequestRepeatAnnotation.class)) {

			Object[] objects = pjp.getArgs();

			for (int i = 0; i < objects.length; i++) {

				Object item = objects[i];

				if (item != null && item instanceof ReqInfo) {
					ReqInfo tmp = (ReqInfo) item;
					String agentId = tmp.getAgentId();
//					String jsonStr = JSONObject.toJSONString(tmp);
					logger.info("RequestRepeatAspect------->method:{},agentId:{}", method, agentId);
//					key->value=当前时间
					long timestamp = System.currentTimeMillis();
//					获取redis key 的组成
					String redisKeyCode= getCode(tmp);
					String key = targetMethod.getName() + "_" + redisKeyCode+ "_" + agentId ;
					boolean flag = lock(key, timestamp);
					if (flag) {
						throw new BusinessException("重复提交,稍后再试");
					}
					
					break;

				}

			}
		}

		return pjp.proceed();
	}

	/**
	 * 根据注解,获取请求参数,根据参数类型获取响应的合同编码,将合同编码作为redis 的key,
	 * 如果请求类型非注解这几种,redisKey 为请求参数所有属性序列化值
	 * @param item
	 * @return
	 */
	private String getCode(Req item) {

		if (item == null) {
			return "";
		}

		String key="";
		try {
			key=item.getUserId()+":token:"+item.getUserToken();
//			key= JSONObject.toJSONString(item);
			
		} catch (Exception e) {
			e.printStackTrace();
			throw new BusinessException(
					"请求参数未填写");
		}
		logger.info("RequestRepeatAspect 获取Code------->getCode:{}",key);

		return key;

	}

	/**
	 * 锁定redis
	 * 
	 * @param key
	 * @param value
	 * @return
	 */
	private boolean lock(String key, long value) {

		// 获取redis key
		byte[] redisKey = getRedisKeys(key);
		byte[] existsValue = cacheManager.get(redisKey);
		// redis 超时时间
		long expireTime = value;
		if (existsValue != null) {
			expireTime = (Long) SerializeUtils.unSerializeAndGunzip(existsValue, Long.class);
		}

		logger.info("lock--->key:{},existsValue:{},value-expireTime:{}",
				SerializeUtils.unSerializeAndGunzip(redisKey, String.class), value, value - expireTime);

		// key 超时
		if (value - expireTime > DEFAULT_EXPIRE_TIME * 60 * 1000) {

			logger.info("lock redis key 超时:--->key:{},expireTime:{}",
					SerializeUtils.unSerializeAndGunzip(redisKey, String.class), expireTime);

			// 移出redis key 信息
			cacheManager.del(redisKey);
		}

		Long existsFlag = cacheManager.setnx(redisKey, SerializeUtils.serializeAndGzip(value));

		logger.info("lock--->existsFlag:{}", existsFlag);

		if (existsFlag == 1) {
			cacheManager.setValueExpireTime(redisKey, DEFAULT_EXPIRE_TIME);
			return false;
		} else {
			return true;
		}

	}

	/**
	 * 获取redis key
	 * 
	 * @param key
	 * @return
	 */
	private static byte[] getRedisKeys(String key) {

		key = StringUtils.isEmpty(key) ? "" : key;
		key = LOCK_TITLE + key;

		return SerializeUtils.serializeAndGzip(key);
	}

}

?

实现方式二:

注解+拦截器icon-default.png?t=M276https://blog.csdn.net/Be_insighted/article/details/119085723?spm=1001.2014.3001.5502

  大数据 最新文章
实现Kafka至少消费一次
亚马逊云科技:还在苦于ETL?Zero ETL的时代
初探MapReduce
【SpringBoot框架篇】32.基于注解+redis实现
Elasticsearch:如何减少 Elasticsearch 集
Go redis操作
Redis面试题
专题五 Redis高并发场景
基于GBase8s和Calcite的多数据源查询
Redis——底层数据结构原理
上一篇文章      下一篇文章      查看所有文章
加:2022-03-22 20:40:57  更:2022-03-22 20:44:29 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 6:28:02-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码