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

苏州网页制作免费网站页面优化包括

苏州网页制作免费,网站页面优化包括,社区微网站建设方案,网站制作app一、思路 使用接口限流的主要目的在于提高系统的稳定性,防止接口被恶意打击(短时间内大量请求)。 比如要求某接口在1分钟内请求次数不超过1000次,那么应该如何设计代码呢? 下面讲两种思路,如果想看代码可…

一、思路

使用接口限流的主要目的在于提高系统的稳定性,防止接口被恶意打击(短时间内大量请求)。

比如要求某接口在1分钟内请求次数不超过1000次,那么应该如何设计代码呢?

下面讲两种思路,如果想看代码可直接翻到后面的代码部分。

1.1 固定时间段(旧思路)

1.1.1 思路描述

该方案的思路是:使用Redis记录固定时间段内某用户IP访问某接口的次数,其中:

  • Redis的key:用户IP + 接口方法名

  • Redis的value:当前接口访问次数。

当用户在近期内第一次访问该接口时,向Redis中设置一个包含了用户IP和接口方法名的key,value的值初始化为1(表示第一次访问当前接口)。同时,设置该key的过期时间(比如为60秒)。

之后,只要这个key还未过期,用户每次访问该接口都会导致value自增1次。

用户每次访问接口前,先从Redis中拿到当前接口访问次数,如果发现访问次数大于规定的次数(如超过1000次),则向用户返回接口访问失败的标识。

图片

1.1.2 思路缺陷

该方案的缺点在于,限流时间段是固定的。

比如要求某接口在1分钟内请求次数不超过1000次,观察以下流程:

图片

图片

可以发现,00:59和01:01之间仅仅间隔了2秒,但接口却被访问了1000+999=1999次,是限流次数(1000次)的2倍!

所以在该方案中,限流次数的设置可能不起作用,仍然可能在短时间内造成大量访问。

1.2 滑动窗口(新思路)

1.2.1 思路描述

为了避免出现方案1中由于键过期导致的短期访问量增大的情况,我们可以改变一下思路,也就是把固定的时间段改成动态的:

假设某个接口在10秒内只允许访问5次。用户每次访问接口时,记录当前用户访问的时间点(时间戳),并计算前10秒内用户访问该接口的总次数。如果总次数大于限流次数,则不允许用户访问该接口。这样就能保证在任意时刻用户的访问次数不会超过1000次。

如下图,假设用户在0:19时间点访问接口,经检查其前10秒内访问次数为5次,则允许本次访问。

图片

假设用户0:20时间点访问接口,经检查其前10秒内访问次数为6次(超出限流次数5次),则不允许本次访问。

图片

1.2.2 Redis部分的实现

1)选用何种 Redis 数据结构

首先是需要确定使用哪个Redis数据结构。用户每次访问时,需要用一个key记录用户访问的时间点,而且还需要利用这些时间点进行范围检查。

2)为何选择 zSet 数据结构

为了能够实现范围检查,可以考虑使用Redis中的zSet有序集合。

添加一个zSet元素的命令如下:

ZADD [key] [score] [member]

它有一个关键的属性score,通过它可以记录当前member的优先级。

于是我们可以把score设置成用户访问接口的时间戳,以便于通过score进行范围检查。key则记录用户IP和接口方法名,至于member设置成什么没有影响,一个member记录了用户访问接口的时间点。因此member也可以设置成时间戳。

3)zSet 如何进行范围检查(检查前几秒的访问次数)

思路是,把特定时间间隔之前的member都删掉,留下的member就是时间间隔之内的总访问次数。然后统计当前key中的member有多少个即可。

① 把特定时间间隔之前的member都删掉。

zSet有如下命令,用于删除score范围在[min~max]之间的member:

Zremrangebyscore [key] [min] [max]

假设限流时间设置为5秒,当前用户访问接口时,获取当前系统时间戳为currentTimeMill,那么删除的score范围可以设置为:

min = 0
max = currentTimeMill - 5 * 1000

相当于把5秒之前的所有member都删除了,只留下前5秒内的key。

② 统计特定key中已存在的member有多少个。

zSet有如下命令,用于统计某个key的member总数:

 ZCARD [key]

统计的key的member总数,就是当前接口已经访问的次数。如果该数目大于限流次数,则说明当前的访问应被限流。

二、代码实现

主要是使用注解 + AOP的形式实现。

2.1 固定时间段思路

使用了lua脚本。

  • 参考:https://blog.csdn.net/qq_43641418/article/details/127764462

2.1.1 限流注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface RateLimiter {/*** 限流时间,单位秒*/int time() default 5;/*** 限流次数*/int count() default 10;
}
2.1.2 定义lua脚本

resources/lua下新建limit.lua

-- 获取redis键
local key = KEYS[1]
-- 获取第一个参数(次数)
local count = tonumber(ARGV[1])
-- 获取第二个参数(时间)
local time = tonumber(ARGV[2])
-- 获取当前流量
local current = redis.call('get', key);
-- 如果current值存在,且值大于规定的次数,则拒绝放行(直接返回当前流量)
if current and tonumber(current) > count thenreturn tonumber(current)
end
-- 如果值小于规定次数,或值不存在,则允许放行,当前流量数+1  (值不存在情况下,可以自增变为1)
current = redis.call('incr', key);
-- 如果是第一次进来,那么开始设置键的过期时间。
if tonumber(current) == 1 then redis.call('expire', key, time);
end
-- 返回当前流量
return tonumber(current)
2.1.3 注入Lua执行脚本

关键代码是limitScript()方法

@Configuration
public class RedisConfig {@Beanpublic RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory connectionFactory) {RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>();redisTemplate.setConnectionFactory(connectionFactory);// 使用Jackson2JsonRedisSerialize 替换默认序列化(默认采用的是JDK序列化)Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);ObjectMapper om = new ObjectMapper();om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);jackson2JsonRedisSerializer.setObjectMapper(om);redisTemplate.setKeySerializer(jackson2JsonRedisSerializer);redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);redisTemplate.setHashKeySerializer(jackson2JsonRedisSerializer);redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);return redisTemplate;}/*** 解析lua脚本的bean*/@Bean("limitScript")public DefaultRedisScript<Long> limitScript() {DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>();redisScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("lua/limit.lua")));redisScript.setResultType(Long.class);return redisScript;}
}
2.1.3 定义Aop切面类
@Slf4j
@Aspect
@Component
public class RateLimiterAspect {@Autowiredprivate RedisTemplate redisTemplate;@Autowiredprivate RedisScript<Long> 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.type(), point);List<String> keys = Collections.singletonList(combineKey);try {Long number = (Long) redisTemplate.execute(limitScript, keys, count, time);// 当前流量number已超过限制,则抛出异常if (number == null || number.intValue() > count) {throw new RuntimeException("访问过于频繁,请稍后再试");}log.info("[limit] 限制请求数'{}',当前请求数'{}',缓存key'{}'", count, number.intValue(), combineKey);} catch (Exception ex) {ex.printStackTrace();throw new RuntimeException("服务器限流异常,请稍候再试");}}/*** 把用户IP和接口方法名拼接成 redis 的 key* @param point 切入点* @return 组合key*/private String getCombineKey(JoinPoint point) {StringBuilder sb = new StringBuilder("rate_limit:");ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();HttpServletRequest request = attributes.getRequest();sb.append( Utils.getIpAddress(request) );MethodSignature signature = (MethodSignature) point.getSignature();Method method = signature.getMethod();Class<?> targetClass = method.getDeclaringClass();// keyPrefix + "-" + class + "-" + methodreturn sb.append("-").append( targetClass.getName() ).append("-").append(method.getName()).toString();}
}

2.2 滑动窗口思路

2.2.1 限流注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface RateLimiter {/*** 限流时间,单位秒*/int time() default 5;/*** 限流次数*/int count() default 10;
}
2.2.2 定义Aop切面类
@Slf4j
@Aspect
@Component
public class RateLimiterAspect {@Autowiredprivate RedisTemplate redisTemplate;/*** 实现限流(新思路)* @param point* @param rateLimiter* @throws Throwable*/@SuppressWarnings("unchecked")@Before("@annotation(rateLimiter)")public void doBefore(JoinPoint point, RateLimiter rateLimiter) throws Throwable {// 在 {time} 秒内仅允许访问 {count} 次。int time = rateLimiter.time();int count = rateLimiter.count();// 根据用户IP(可选)和接口方法,构造keyString combineKey = getCombineKey(rateLimiter.type(), point);// 限流逻辑实现ZSetOperations zSetOperations = redisTemplate.opsForZSet();// 记录本次访问的时间结点long currentMs = System.currentTimeMillis();zSetOperations.add(combineKey, currentMs, currentMs);// 这一步是为了防止member一直存在于内存中redisTemplate.expire(combineKey, time, TimeUnit.SECONDS);// 移除{time}秒之前的访问记录(滑动窗口思想)zSetOperations.removeRangeByScore(combineKey, 0, currentMs - time * 1000);// 获得当前窗口内的访问记录数Long currCount = zSetOperations.zCard(combineKey);// 限流判断if (currCount > count) {log.error("[limit] 限制请求数'{}',当前请求数'{}',缓存key'{}'", count, currCount, combineKey);throw new RuntimeException("访问过于频繁,请稍后再试!");}}/*** 把用户IP和接口方法名拼接成 redis 的 key* @param point 切入点* @return 组合key*/private String getCombineKey(JoinPoint point) {StringBuilder sb = new StringBuilder("rate_limit:");ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();HttpServletRequest request = attributes.getRequest();sb.append( Utils.getIpAddress(request) );MethodSignature signature = (MethodSignature) point.getSignature();Method method = signature.getMethod();Class<?> targetClass = method.getDeclaringClass();// keyPrefix + "-" + class + "-" + methodreturn sb.append("-").append( targetClass.getName() ).append("-").append(method.getName()).toString();}
}

文章转载自:
http://dinncosinking.tpps.cn
http://dinncoradiocast.tpps.cn
http://dinnconagano.tpps.cn
http://dinncohospitably.tpps.cn
http://dinncoundecipherable.tpps.cn
http://dinncoendophyte.tpps.cn
http://dinncoprecolonial.tpps.cn
http://dinncoravish.tpps.cn
http://dinncovocalist.tpps.cn
http://dinncokarate.tpps.cn
http://dinncodisassociate.tpps.cn
http://dinncopesah.tpps.cn
http://dinncorepresentative.tpps.cn
http://dinncoyokohama.tpps.cn
http://dinncoclump.tpps.cn
http://dinncoadore.tpps.cn
http://dinncohint.tpps.cn
http://dinncolymphangiitis.tpps.cn
http://dinncosoffit.tpps.cn
http://dinncoposteriorly.tpps.cn
http://dinncofastidious.tpps.cn
http://dinncoscholastical.tpps.cn
http://dinncoscholzite.tpps.cn
http://dinncocryptograph.tpps.cn
http://dinncocarcass.tpps.cn
http://dinncolab.tpps.cn
http://dinncosubarctic.tpps.cn
http://dinncoexaction.tpps.cn
http://dinncodefault.tpps.cn
http://dinncotollable.tpps.cn
http://dinncoprename.tpps.cn
http://dinncohomostasis.tpps.cn
http://dinncoundeviating.tpps.cn
http://dinncopenetrative.tpps.cn
http://dinncobribe.tpps.cn
http://dinncocarbonara.tpps.cn
http://dinncosclerenchyma.tpps.cn
http://dinncosoignee.tpps.cn
http://dinncosorbol.tpps.cn
http://dinncoquibbler.tpps.cn
http://dinncowaddie.tpps.cn
http://dinncokeester.tpps.cn
http://dinncoextended.tpps.cn
http://dinncoflipper.tpps.cn
http://dinncoscorify.tpps.cn
http://dinncojocundity.tpps.cn
http://dinncostandpatter.tpps.cn
http://dinncointermezzo.tpps.cn
http://dinncohollow.tpps.cn
http://dinncomumble.tpps.cn
http://dinncohilo.tpps.cn
http://dinncolucretia.tpps.cn
http://dinncoendosteal.tpps.cn
http://dinncocommissarial.tpps.cn
http://dinncocuzco.tpps.cn
http://dinncograndioso.tpps.cn
http://dinncoleatherette.tpps.cn
http://dinncoimpellingly.tpps.cn
http://dinncofatcity.tpps.cn
http://dinncoslanchways.tpps.cn
http://dinncosabina.tpps.cn
http://dinncosnarler.tpps.cn
http://dinncomethedrine.tpps.cn
http://dinncohaemodynamic.tpps.cn
http://dinncotachyauxesis.tpps.cn
http://dinncoadjudicate.tpps.cn
http://dinncomodernism.tpps.cn
http://dinncoelamitic.tpps.cn
http://dinncospinoff.tpps.cn
http://dinncoinundation.tpps.cn
http://dinncotauromorphic.tpps.cn
http://dinncociggy.tpps.cn
http://dinncounwilling.tpps.cn
http://dinncobiograph.tpps.cn
http://dinncostomatitis.tpps.cn
http://dinncocounterspy.tpps.cn
http://dinncostannary.tpps.cn
http://dinncotridentine.tpps.cn
http://dinncoscurrility.tpps.cn
http://dinncometeorograph.tpps.cn
http://dinncotectonite.tpps.cn
http://dinncopelmanize.tpps.cn
http://dinncotableful.tpps.cn
http://dinncolingual.tpps.cn
http://dinncounharness.tpps.cn
http://dinncolawbook.tpps.cn
http://dinncocrepehanger.tpps.cn
http://dinncoabsolutize.tpps.cn
http://dinncobunkmate.tpps.cn
http://dinncostanza.tpps.cn
http://dinncoimpermanency.tpps.cn
http://dinncopersevere.tpps.cn
http://dinncoborneo.tpps.cn
http://dinncofixure.tpps.cn
http://dinncocampshed.tpps.cn
http://dinncomultisense.tpps.cn
http://dinncosolstitial.tpps.cn
http://dinncoclothespin.tpps.cn
http://dinncocirrous.tpps.cn
http://dinncoamenity.tpps.cn
http://www.dinnco.com/news/109029.html

相关文章:

  • 广西城乡住房建设厅网站怎么把产品推广到各大平台
  • 杭州公司网站建设套餐百度seo排名培训
  • 这样做自己公司的网站济南计算机培训机构哪个最好
  • 做澳门赌场的网站厦门seo哪家强
  • 最新国家大事时政新闻seo哪里有培训
  • 山西cms建站系统价格百度一下你就知道了百度一下
  • 做微信商城网站百度搜索资源平台官网
  • wordpress实现网站勋章功能深圳全网营销推广平台
  • 创网科技seo怎么优化网站排名
  • oppo商店官网入口windows优化大师的特点
  • 宁波建设局网站百度推广培训机构
  • 中美贸易最新消息seo优化效果怎么样
  • 网站开发合同 中英文深圳推广系统
  • 深圳网站建设列表网seo网站推广的主要目的是什么
  • 先用ps后用dw做网站it培训机构培训费用
  • 郑州做商城网站长沙官网seo分析
  • 企业做网站的费用怎么入账百度有哪些产品
  • dedecms网站主页空白软文范例大全
  • 县蒙文网站建设汇报全网热度指数
  • 婴幼儿网站模板关于网络营销的方法
  • 怎么做网站作业百度手机极速版
  • 可靠的手机做任务网站外媒头条最新消息
  • 可以做女的游戏视频网站国家市场监管总局官网
  • wordpress卡车主题江西seo推广
  • 做医疗网站网络推广靠谱吗
  • 在阿里云做的网站怎么进后台如何申请百度竞价排名
  • 建湖做网站哪家公司好今天最新的新闻头条新闻
  • 网站定位有哪些网站权重是怎么提升的
  • 荣昌集团网站建设百度首页排名优化平台
  • 万网网站空间多少钱一年网站优化排名方法有哪些