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

做棋牌网站多少钱合肥房产网

做棋牌网站多少钱,合肥房产网,做模拟人生类的游戏下载网站,做影视网站引流一、思路 使用接口限流的主要目的在于提高系统的稳定性#xff0c;防止接口被恶意打击#xff08;短时间内大量请求#xff09;。 比如要求某接口在1分钟内请求次数不超过1000次#xff0c;那么应该如何设计代码呢#xff1f; 下面讲两种思路#xff0c;如果想看代码可…一、思路 使用接口限流的主要目的在于提高系统的稳定性防止接口被恶意打击短时间内大量请求。 比如要求某接口在1分钟内请求次数不超过1000次那么应该如何设计代码呢 下面讲两种思路如果想看代码可直接翻到后面的代码部分。 1.1 固定时间段旧思路 1.1.1 思路描述 该方案的思路是使用Redis记录固定时间段内某用户IP访问某接口的次数其中 Redis的key用户IP 接口方法名 Redis的value当前接口访问次数。 当用户在近期内第一次访问该接口时向Redis中设置一个包含了用户IP和接口方法名的keyvalue的值初始化为1表示第一次访问当前接口。同时设置该key的过期时间比如为60秒。 之后只要这个key还未过期用户每次访问该接口都会导致value自增1次。 用户每次访问接口前先从Redis中拿到当前接口访问次数如果发现访问次数大于规定的次数如超过1000次则向用户返回接口访问失败的标识。 1.1.2 思路缺陷 该方案的缺点在于限流时间段是固定的。 比如要求某接口在1分钟内请求次数不超过1000次观察以下流程 可以发现00:59和01:01之间仅仅间隔了2秒但接口却被访问了10009991999次是限流次数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也可以设置成时间戳。 3zSet 如何进行范围检查检查前几秒的访问次数 思路是把特定时间间隔之前的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 RedisTemplateObject, Object redisTemplate(RedisConnectionFactory connectionFactory) {RedisTemplateObject, Object redisTemplate  new RedisTemplate();redisTemplate.setConnectionFactory(connectionFactory);// 使用Jackson2JsonRedisSerialize 替换默认序列化(默认采用的是JDK序列化)Jackson2JsonRedisSerializerObject 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 DefaultRedisScriptLong limitScript() {DefaultRedisScriptLong 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 RedisScriptLong 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);ListString 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://www.dnsts.com.cn/news/4654.html

相关文章:

  • 泉州网站建设公司首选公司外贸工具大全网站
  • 烟台网站制作公司哪家好产品研发的流程和步骤
  • 网站建设公司有哪些原疫苗最新官方消息
  • 免费建手机商城网站佛山专业网站推广公司
  • 域名解析步骤网站建设优化服务策划
  • 建设网站的公司排名wordpress怎么取当前点击的tag
  • 个人网站建设案例课堂郴州网站建设ku0735
  • 建设网站账务处理济南浩特元辰建设工程有限公司网站
  • 建立网站需要多少钱稻挺湖南岚鸿有名广告营销策划案
  • 锦州网站建设推广广州知名网站建设有哪些
  • 想做一个自己设计公司的网站怎么做的上海公司推荐
  • 合肥网站制作费用网站制作与网站建设实际报告
  • 网站建设服安徽建设工程信息网监理查询
  • 网站教人做核能灯旧手机服务器wordpress
  • 精美的微网站能够做冶金工程毕业设计的网站
  • 网站实现功能网页游戏代理平台
  • qq空间认证的网站后台根目录中小企业erp软件排名
  • 天津网站建设索王道下拉房产中介如何找客源
  • 可以看所有网站的浏览器佛山关键词网站排名
  • 代码做网站图片怎么插深圳博大建设公司
  • 响应式 网站 开发价格低英语翻译
  • vue做网站前台网站建设技术可行性
  • 电子书网站用dz还是wordpresswordpress是啥东西
  • 四川建设厅招投标官方网站wordpress新闻墙插件
  • 企业网站网页布局中国网络营销传播网
  • phpcms 网站访问统计哪个网站做废旧好
  • 网站做的不满意网站的设计流程有哪些步骤
  • 重庆建网站推广价格官网优化公司
  • 网站排名优化软件小程序商店制作教程
  • 开题报告风景区网站开发微信公众号人工服务电话