当前位置: 首页 > news >正文

怎么做软文链接打开后是自定义网站什么关键词可以搜到那种

怎么做软文链接打开后是自定义网站,什么关键词可以搜到那种,深圳市做网站公司,乌鲁木齐广告设计制作文章目录 Redis多规则限流和防重复提交记录访问次数解决临界值访问问题实现多规则限流先确定最终需要的效果编写注解(RateLimiter,RateRule)拦截注解 RateLimiter 编写lua脚本UUID时间戳编写 AOP 拦截 总结 Redis多规则限流和防重复提交 市面…

文章目录

  • Redis多规则限流和防重复提交
    • 记录访问次数
    • 解决临界值访问问题
    • 实现多规则限流
      • 先确定最终需要的效果
      • 编写注解(RateLimiter,RateRule)
      • 拦截注解 RateLimiter
    • 编写lua脚本
      • UUID
      • 时间戳
      • 编写 AOP 拦截
    • 总结

Redis多规则限流和防重复提交

市面上很多介绍redis如何实现限流的,但是大部分都有一个缺点,就是只能实现单一的限流,比如1分钟访问1次或者60分钟访问10次这种,但是如果想一个接口两种规则都需要满足呢,我们的项目又是分布式项目,应该如何解决,下面就介绍一下redis实现分布式多规则限流的方式。

  • 如何一分钟只能发送一次验证码,一小时只能发送10次验证码等等多种规则的限流?
  • 如何防止接口被恶意打击(短时间内大量请求)?
  • 如何限制接口规定时间内访问次数?

记录访问次数

使用 String 结构,记录固定时间段内某用户IP访问某接口的次数
RedisKey = prefix : className : methodName
RedisVlue = 访问次数

拦截请求:

  1. 初次访问时设置 [RedisKey] [RedisValue=1] [规定的过期时间]
  2. 获取 RedisValue 是否超过规定次数,超过则拦截,未超过则对 RedisKey 进行加1

分析: 规则是每分钟访问 1000 次

  1. 考虑并发问题
    • 假设目前 RedisKey => RedisValue 为 999
    • 目前大量请求进行到第一步( 获取Redis请求次数 ),那么所有线程都获取到了值为999,进行判断都未超过限定次数则不拦截,导致实际次数超过 1000 次
    • 解决办法: 保证方法执行原子性(加锁、lua)
  2. 考虑在临界值进行访问
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IL4u0jaY-1721999915731)(https://i-blog.csdnimg.cn/direct/1eacf46030b6471e91ce43d1e5eae900.png)]
import java.lang.reflect.Method;
import java.util.Collections;
import java.util.List;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.RedisScript;
import org.springframework.stereotype.Component;
import com.ruoyi.common.annotation.RateLimiter;
import com.ruoyi.common.enums.LimitType;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.ip.IpUtils;/*** 限流处理*/
@Aspect
@Component
public class RateLimiterAspect
{private static final Logger log = LoggerFactory.getLogger(RateLimiterAspect.class);private RedisTemplate<Object, Object> redisTemplate;private RedisScript<Long> limitScript;@Autowiredpublic void setRedisTemplate1(RedisTemplate<Object, Object> redisTemplate){this.redisTemplate = redisTemplate;}@Autowiredpublic void setLimitScript(RedisScript<Long> limitScript){this.limitScript = limitScript;}@Before("@annotation(rateLimiter)")public void doBefore(JoinPoint point, RateLimiter rateLimiter) throws Throwable{int time = rateLimiter.time();int count = rateLimiter.count();String combineKey = getCombineKey(rateLimiter, point);List<Object> keys = Collections.singletonList(combineKey);try{Long number = redisTemplate.execute(limitScript, keys, count, time);if (StringUtils.isNull(number) || number.intValue() > count){throw new ServiceException("访问过于频繁,请稍候再试");}log.info("限制请求'{}',当前请求'{}',缓存key'{}'", count, number.intValue(), combineKey);}catch (ServiceException e){throw e;}catch (Exception e){throw new RuntimeException("服务器限流异常,请稍候再试");}}public String getCombineKey(RateLimiter rateLimiter, JoinPoint point){StringBuffer stringBuffer = new StringBuffer(rateLimiter.key());if (rateLimiter.limitType() == LimitType.IP){stringBuffer.append(IpUtils.getIpAddr()).append("-");}MethodSignature signature = (MethodSignature) point.getSignature();Method method = signature.getMethod();Class<?> targetClass = method.getDeclaringClass();stringBuffer.append(targetClass.getName()).append("-").append(method.getName());return stringBuffer.toString();}
}

解决临界值访问问题

使用 Zset 进行存储,解决临界值访问问题。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-frPzmAdy-1721999915734)(https://i-blog.csdnimg.cn/direct/1ce98a4076d343f69e7ed880eff292bf.png)]

实现多规则限流

先确定最终需要的效果

  • 能实现多种限流规则
  • 能实现防重复提交
    通过以上要求设计注解(先想象出最终实现效果)
@RateLimiter(rules = {// 60秒内只能访问10次@RateRule(count = 10, time = 60, timeUnit = TimeUnit.SECONDS),// 120秒内只能访问20次@RateRule(count = 20, time = 120, timeUnit = TimeUnit.SECONDS)},// 防重复提交 (5秒钟只能访问1次)preventDuplicate = true
)

编写注解(RateLimiter,RateRule)

编写 RateLimiter 注解

/*** @Description: 请求接口限制*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface RateLimiter {/*** 限流key*/String key() default RedisKeyConstants.RATE_LIMIT_CACHE_PREFIX;/*** 限流类型 ( 默认 Ip 模式 )*/LimitTypeEnum limitType() default LimitTypeEnum.IP;/*** 错误提示*/ResultCode message() default ResultCode.REQUEST_MORE_ERROR;/*** 限流规则 (规则不可变,可多规则)*/RateRule[] rules() default {};/*** 防重复提交值*/boolean preventDuplicate() default false;/*** 防重复提交默认值*/RateRule preventDuplicateRule() default @RateRule(count = 1, time = 5);
}

编写 RateRule 注解

@Target(ElementType.ANNOTATION_TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Inherited
public @interface RateRule {/*** 限流次数*/long count() default 10;/*** 限流时间*/long time() default 60;/*** 限流时间单位*/TimeUnit timeUnit() default TimeUnit.SECONDS;
}

拦截注解 RateLimiter

  • 确定redis存储方式
    • RedisKey = prefix : className : methodName
    • RedisScore = 时间戳
    • RedisValue = 任意分布式不重复的值即可
  • 编写生成 RedisKey 的方法
/*** 通过 rateLimiter 和 joinPoint 拼接  prefix : ip / userId : classSimpleName - methodName** @param rateLimiter 提供 prefix* @param joinPoint   提供 classSimpleName : methodName* @return*/
public String getCombineKey(RateLimiter rateLimiter, JoinPoint joinPoint) {StringBuffer key = new StringBuffer(rateLimiter.key());// 不同限流类型使用不同的前缀switch (rateLimiter.limitType()) {// XXX 可以新增通过参数指定参数进行限流case IP:key.append(IpUtil.getIpAddr(((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest())).append(":");break;case USER_ID:SysUserDetails user = SecurityUtil.getUser();if (!ObjectUtils.isEmpty(user)) key.append(user.getUserId()).append(":");break;case GLOBAL:break;}MethodSignature signature = (MethodSignature) joinPoint.getSignature();Method method = signature.getMethod();Class<?> targetClass = method.getDeclaringClass();key.append(targetClass.getSimpleName()).append("-").append(method.getName());return key.toString();
}

编写lua脚本

两种将时间添加到Redis的方法。

UUID

UUID(可用其他有相同的特性的值)为Zset中的value值

  • 参数介绍
    • KEYS[1] = prefix : ? : className : methodName
    • KEYS[2] = 唯一ID
    • KEYS[3] = 当前时间
    • ARGV = [次数,单位时间,次数,单位时间, 次数, 单位时间 ...]
  • java传入分布式不重复的 value
-- 1. 获取参数
local key = KEYS[1]
local uuid = KEYS[2]
local currentTime = tonumber(KEYS[3])
-- 2. 以数组最大值为 ttl 最大值
local expireTime = -1;
-- 3. 遍历数组查看是否超过限流规则
for i = 1, #ARGV, 2 dolocal rateRuleCount = tonumber(ARGV[i])local rateRuleTime = tonumber(ARGV[i + 1])-- 3.1 判断在单位时间内访问次数local count = redis.call('ZCOUNT', key, currentTime - rateRuleTime, currentTime)-- 3.2 判断是否超过规定次数if tonumber(count) >= rateRuleCount thenreturn trueend-- 3.3 判断元素最大值,设置为最终过期时间if rateRuleTime > expireTime thenexpireTime = rateRuleTimeend
end
-- 4. redis 中添加当前时间
redis.call('ZADD', key, currentTime, uuid)
-- 5. 更新缓存过期时间
redis.call('PEXPIRE', key, expireTime)
-- 6. 删除最大时间限度之前的数据,防止数据过多
redis.call('ZREMRANGEBYSCORE', key, 0, currentTime - expireTime)
return false

时间戳

根据时间戳作为Zset中的value值

  • 参数介绍
    • KEYS[1] = prefix : ? : className : methodName
    • KEYS[2] = 当前时间
    • ARGV = [次数,单位时间,次数,单位时间, 次数, 单位时间 ...]
  • 根据时间进行生成value值,考虑同一毫秒添加相同时间值问题
    • 以下为第二种实现方式,在并发高的情况下效率低,value是通过时间戳进行添加,但是访问量大的话会使得一直在调用 redis.call('ZADD', key, currentTime, currentTime),但是在不冲突value的情况下,会比生成 UUID
-- 1. 获取参数
local key = KEYS[1]
local currentTime = KEYS[2]
-- 2. 以数组最大值为 ttl 最大值
local expireTime = -1;
-- 3. 遍历数组查看是否越界
for i = 1, #ARGV, 2 dolocal rateRuleCount = tonumber(ARGV[i])local rateRuleTime = tonumber(ARGV[i + 1])-- 3.1 判断在单位时间内访问次数local count = redis.call('ZCOUNT', key, currentTime - rateRuleTime, currentTime)-- 3.2 判断是否超过规定次数if tonumber(count) >= rateRuleCount thenreturn trueend-- 3.3 判断元素最大值,设置为最终过期时间if rateRuleTime > expireTime thenexpireTime = rateRuleTimeend
end
-- 4. 更新缓存过期时间
redis.call('PEXPIRE', key, expireTime)
-- 5. 删除最大时间限度之前的数据,防止数据过多
redis.call('ZREMRANGEBYSCORE', key, 0, currentTime - expireTime)
-- 6. redis 中添加当前时间  ( 解决多个线程在同一毫秒添加相同 value 导致 Redis 漏记的问题 )
-- 6.1 maxRetries 最大重试次数 retries 重试次数
local maxRetries = 5
local retries = 0
while true dolocal result = redis.call('ZADD', key, currentTime, currentTime)if result == 1 then-- 6.2 添加成功则跳出循环breakelse-- 6.3 未添加成功则 value + 1 再次进行尝试retries = retries + 1if retries >= maxRetries then-- 6.4 超过最大尝试次数 采用添加随机数策略local random_value = math.random(1, 1000)currentTime = currentTime + random_valueelsecurrentTime = currentTime + 1endend
endreturn false

编写 AOP 拦截

@Autowired
private RedisTemplate<String, Object> redisTemplate;@Autowired
private RedisScript<Boolean> limitScript;/*** 限流* XXX 对限流要求比较高,可以使用在 Redis中对规则进行存储校验 或者使用中间件** @param joinPoint   joinPoint* @param rateLimiter 限流注解*/
@Before(value = "@annotation(rateLimiter)")
public void boBefore(JoinPoint joinPoint, RateLimiter rateLimiter) {// 1. 生成 keyString key = getCombineKey(rateLimiter, joinPoint);try {// 2. 执行脚本返回是否限流Boolean flag = redisTemplate.execute(limitScript,ListUtil.of(key, String.valueOf(System.currentTimeMillis())),(Object[]) getRules(rateLimiter));// 3. 判断是否限流if (Boolean.TRUE.equals(flag)) {log.error("ip: '{}' 拦截到一个请求 RedisKey: '{}'",IpUtil.getIpAddr(((ServletRequestAttributes) Objects.requireNonNull(RequestContextHolder.getRequestAttributes())).getRequest()),key);throw new ServiceException(rateLimiter.message());}} catch (ServiceException e) {throw e;} catch (Exception e) {e.printStackTrace();}
}/*** 获取规则** @param rateLimiter 获取其中规则信息* @return*/
private Long[] getRules(RateLimiter rateLimiter) {int capacity = rateLimiter.rules().length << 1;// 1. 构建 argsLong[] args = new Long[rateLimiter.preventDuplicate() ? capacity + 2 : capacity];// 3. 记录数组元素int index = 0;// 2. 判断是否需要添加防重复提交到redis进行校验if (rateLimiter.preventDuplicate()) {RateRule preventRateRule = rateLimiter.preventDuplicateRule();args[index++] = preventRateRule.count();args[index++] = preventRateRule.timeUnit().toMillis(preventRateRule.time());}RateRule[] rules = rateLimiter.rules();for (RateRule rule : rules) {args[index++] = rule.count();args[index++] = rule.timeUnit().toMillis(rule.time());}return args;
}

总结

为了实现多规则限流和防止重复提交,我们可以采用Redis作为后端存储,结合Lua脚本来确保原子性和准确性。下面是基于你的需求和提供的示例代码的详细总结:

设计目标

  • 多规则限流:实现多种不同时间范围内的访问频率控制。
  • 防止重复提交:在一定时间内限制同一请求的重复提交。

方案概述

  1. 使用String结构记录访问次数:适用于简单限流,但容易遇到并发问题。
  2. 使用ZSet结构:解决并发问题,并支持多规则限流。
  3. 编写Lua脚本:用于高效地处理多规则限流逻辑。
  4. AOP拦截器:在Spring框架中使用AspectJ进行请求拦截。

限流规则

  • 规则定义:通过自定义注解@RateLimiter@RateRule来指定限流规则和防重复提交的策略。
  • 规则应用:通过AspectJ切面编程在方法执行前进行检查。

Redis键值设计

  • prefix : ? : className : methodName
  • 分值:时间戳
  • 成员:唯一标识符(如UUID)或时间戳

Lua脚本实现

  • 使用UUID:解决并发问题的同时避免数据冗余。

  • 使用时间戳:简化脚本实现,但需要解决同一毫秒内多个请求的问题。

  • 多规则限流:通过使用ZSet结构和Lua脚本实现。

  • 防重复提交:通过在规则中添加额外的限流规则实现。

  • 并发安全:Lua脚本确保了限流逻辑的原子性。

  • 性能优化:通过预先计算过期时间,减少不必要的Redis命令调用。

这样,你可以有效地在分布式系统中实现多规则限流和防止重复提交。


文章转载自:
http://lockage.qpqb.cn
http://glaringly.qpqb.cn
http://sandhurst.qpqb.cn
http://beautician.qpqb.cn
http://foreleg.qpqb.cn
http://loudish.qpqb.cn
http://lanoline.qpqb.cn
http://plu.qpqb.cn
http://crescive.qpqb.cn
http://sailor.qpqb.cn
http://qualifier.qpqb.cn
http://juniorate.qpqb.cn
http://brainteaser.qpqb.cn
http://smokable.qpqb.cn
http://duykerbok.qpqb.cn
http://siphonein.qpqb.cn
http://nesting.qpqb.cn
http://radioceramic.qpqb.cn
http://boko.qpqb.cn
http://panterer.qpqb.cn
http://henceforth.qpqb.cn
http://schistocyte.qpqb.cn
http://hadron.qpqb.cn
http://archaeology.qpqb.cn
http://bock.qpqb.cn
http://sunderland.qpqb.cn
http://filbert.qpqb.cn
http://unseasoned.qpqb.cn
http://dichroscope.qpqb.cn
http://coenacle.qpqb.cn
http://metallurgy.qpqb.cn
http://exosphere.qpqb.cn
http://risc.qpqb.cn
http://msae.qpqb.cn
http://internuncial.qpqb.cn
http://colistin.qpqb.cn
http://capri.qpqb.cn
http://soubriquet.qpqb.cn
http://kasbah.qpqb.cn
http://boo.qpqb.cn
http://x.qpqb.cn
http://reune.qpqb.cn
http://gibbose.qpqb.cn
http://sensational.qpqb.cn
http://dactyl.qpqb.cn
http://hypothyroidism.qpqb.cn
http://before.qpqb.cn
http://bathochrome.qpqb.cn
http://upstairs.qpqb.cn
http://twiggy.qpqb.cn
http://serpent.qpqb.cn
http://computator.qpqb.cn
http://jailer.qpqb.cn
http://unshapely.qpqb.cn
http://spindling.qpqb.cn
http://satyrid.qpqb.cn
http://sally.qpqb.cn
http://reprobation.qpqb.cn
http://katharevousa.qpqb.cn
http://revolvably.qpqb.cn
http://nuque.qpqb.cn
http://cablephoto.qpqb.cn
http://multipotent.qpqb.cn
http://supermart.qpqb.cn
http://armour.qpqb.cn
http://leveling.qpqb.cn
http://technician.qpqb.cn
http://unofficious.qpqb.cn
http://abasia.qpqb.cn
http://cholecystectomized.qpqb.cn
http://sematic.qpqb.cn
http://blackcap.qpqb.cn
http://postfigurative.qpqb.cn
http://interclavicle.qpqb.cn
http://cis.qpqb.cn
http://powerman.qpqb.cn
http://trillionth.qpqb.cn
http://grainsick.qpqb.cn
http://neon.qpqb.cn
http://livability.qpqb.cn
http://archaeological.qpqb.cn
http://carrageenan.qpqb.cn
http://westernize.qpqb.cn
http://miter.qpqb.cn
http://laa.qpqb.cn
http://glycogenolysis.qpqb.cn
http://lysogenize.qpqb.cn
http://denunciate.qpqb.cn
http://megadont.qpqb.cn
http://exegetic.qpqb.cn
http://knockout.qpqb.cn
http://atmology.qpqb.cn
http://volos.qpqb.cn
http://transection.qpqb.cn
http://researcher.qpqb.cn
http://pontoon.qpqb.cn
http://cartopper.qpqb.cn
http://heptameter.qpqb.cn
http://isometropia.qpqb.cn
http://rhinostegnosis.qpqb.cn
http://www.dt0577.cn/news/23397.html

相关文章:

  • 建站群赚钱有前途吗怎样和政府交换友链
  • 商城网站建站怎么免费建个人网站
  • 盐城做网站企业seo先上排名后收费
  • 长沙专业网站建设公司排名百度收录快的发帖平台
  • 合肥网站建设模板7个湖北seo网站推广策略
  • dw怎么把代码做成网页搜索引擎优化通常要注意的问题有
  • 企业门户网站作用百度广告投放平台
  • 婚车租赁网站怎样做sem公司
  • 做调查赚钱靠谱的网站有哪些网络营销师怎么考
  • 徐州网站建设找哪家好seo外包如何
  • 下载企业微信最新版惠州seo优化服务
  • 科技公司 网站模板营销网
  • 什么公司在百度做网站seo关键词排名软件流量词
  • 网站开发技术考题网站建设制作流程
  • 免费的网站域名查询浏览器网推团队
  • 优质聊城做网站费用建一个网站大概需要多少钱
  • 页眉做的好的网站关键词搜索排名工具
  • 做seo网站诊断书怎么做优质友情链接
  • 国外档案网站建设互联网广告联盟
  • 专业做淘宝网站推广南京seo公司哪家
  • 有哪些做ppt用图片的网站有哪些百度排名怎么做
  • wordpress 中国 论坛引擎seo优
  • 网站制作工具有哪些纹绣培训班一般价格多少
  • 订阅号可以做网站链接吗广州网站排名推广
  • 文化建设设计网站肇庆seo排名外包
  • 做网站赚不到钱了国际新闻最新消息十条
  • 内部网站建设的步骤过程打开百度搜索网站
  • 外贸网站建设公司青岛seo优化外链平台
  • 网站开发主流技术线路介绍谷歌浏览器网页
  • 学做网站需要什么基础连云港seo优化