您当前的位置:首页 > 电脑百科 > 站长技术 > 服务器

注解式限流是如何实现的?

时间:2019-12-03 10:00:23  来源:  作者:

一个问题往往会引出了一连串的问题,知识的盲区就这样被自己悄悄的发现了。车辙在自己动手写限流注解时,遇到的问题那是真一个比一个多:

  1. 限流算法用哪个比较合适。
  2. 如何用注解实现限流。
  3. 如何对每个方法单独限流。
  4. 长字符串如何转换成短字符串。
  5. 64 进制 or 62进制。
  6. LRU 是什么,如何用简单的数据结构实现。

什么是限流

对服务器接收到的请求作出限制,只有一部分请求能真正到达服务器,其他的请求可以延迟,也可以拒绝。从而避免所有请求到数据库,打垮 DB。

举个生活中大家可能遇到的场景,特别是北上广深或者新一线城市,杭州一号线地铁,凤起路站,在客流量到达一定峰值时,警察叔叔‍♀可能就不让你进地铁,让使用其他交通工具了️。。。都是泪啊!

限流算法用哪个比较合适

关于限流算法,网上的解释一大堆,漏桶算法,令牌桶算法等等,百度一下,你就知道,在这里车辙用最简单的计数器算法作为实现。

计数器算法

  1. 将一秒钟分为 10 个阶段,每个阶段 100ms。
  2. 每隔 100ms 记录下接口调用的次数。
  3. 当然随着时间的流逝,阶段会越来越多。这时候可以将最前面的 n 个阶段删除,只保留 10 个,也就是只剩 1s。
  4. 最后一个减去第一个的次数,就是 1s 中内该接口调用的次数。
注解式限流是如何实现的?

 

 

如何用注解实现限流

在用 Nginx 限流时,是将 nginx 作为代理层拦截请求处理,那么在 Spring 中代理层就是 AOP 啦。

AOP

在 web 服务器中,有很多场景都是可以靠 AOP 实现的,比如:

  1. 打印日志,记录时间类,方法,参数。
  2. 利用反射设置分页 PageRow、PageNum 的默认值。
  3. 游戏场景,判断游戏是否已经结束,不用每个方法都去判断。
  4. 解密,验签等等。

定时任务

在计数器算法中我们提到,每隔 100ms 需要记录接口调用的次数,并保存。这时候定时任务就派上用场了。

定时任务的实现有很多,像利用线程池的 ScheduledExecutorService,当然 Spring 的 Scheduled 也莫得问题。

其次,用什么数据结构保存调用次数 --> LinkedList。

另外,我们需要对多个方法限流,该如何解决呢?--> 每个方法都有唯一对应的值: package + class + methodName,于是我们将这个唯一值作为key,linkedList 作为 map,下方代码:

 1 /** 每个key 对应的调用次数**/
 2 private Map<String, Long> countMap = new ConcurrentHashMap<>();
 3
 4 /** 每个key 对应的linkedlist**/
 5 private static Map<String, LinkedList<Long>> calListMap = new ConcurrentHashMap<>();
 6
 7 ## 每s一次查询
 8 @Scheduled(cron = "*/1 * * * * ?")
 9 private void timeGet(){
10 countMap.forEach((k,v)->{
11 LinkedList<Long> calList = calListMap.get(k);
12 if(calList == null){
13 calList = new LinkedList<>();
14 }
15 # 每个方法的调用次数放入linkedList中
16 calList.addLast(v);
17 calListMap.put(k, calList);
18
19 if (calList.size() > 10) {
20 calList.removeFirst();
21 }
22 });
23 }

AOP 检查

定义注解:

 1import JAVA.lang.annotation.*;
 2
 3
 4@Target(ElementType.METHOD)
 5@Retention(RetentionPolicy.RUNTIME)
 6@Documented
 7public @interface CalLimitAnno {
 8
 9 String value() default "" ;
10
11 String methodName() default "" ;
12
13 long count() default 100;
14}

调用接口前检查:

 1@Around(value = "@annotation(around)")
 2 public Object initBean(ProceedingJoinPoint point, CalLimitAnno around) throws Throwable {
 3 /** 获取类名和方法名 **/
 4 MethodSignature signature = (MethodSignature) point.getSignature();
 5 Method method = signature.getMethod();
 6 String[] classNameArray = method.getDeclaringClass().getName().split("\.");
 7 String methodName = classNameArray[classNameArray.length - 1] + "." + method.getName();
 8 String classZ = signature.getDeclaringTypeName();
 9 String countMapKey = classZ + "|" + methodName;
10
11
12 LinkedList<Long> calList = calListMap.get(countMapKey);
13 if(calList != null){
14 /** 调用次数判断是否已经超过注解设置的值 **/
15 if ((calList.peekLast() - calList.peekFirst()) > Long.valueOf(around.count())) {
16 throw new RuntimeException("被限流了");
17 }
18 /** 存放**/
19 countMap.putIfAbsent(countMapKey,0L);
20 countMap.put(countMapKey,countMap.get(countMapKey) + 1);
21 }
22 Object object = point.proceed();
23 return object;
24 }

方法考虑到定时任务的频率不能太小,因此我们的定时任务是每秒钟执行一次,这里我们需要设置 10s 钟的限流值,导致粒度变大了。

1@CalLimitAnno(count = 1000)
2 public void testPageAnno(){
3 System.out.println("成功执行");
4 }

Map 优化

上述我们将 package + className + methodName 作为唯一 key,导致 key 的长度变得特别长,我们是不是该想个办法降低 key 的长度。

大家有没有想到平时收到的短信,有时候会存在一个短链接,这些短连接其实就是用的发号器 --> 从某个服务中获取唯一的自增id,然后将这个 id 进行转化。比如这时候自增到 100000 了,那么将 100000 从十进制转化为 62 进制 q0U。这个和短信上的链接很相似不是吗?

Map 持久化

既然是自增的,那么相同的长字符通过调用服务转化成的短字符串都是不同的。在某些业务场景,可能调用比较频繁,就需要做kv存储。不然也没有必要做存储了,多做多错嘛~

kv 存储优化

假设我们需要做 kv 存储,童鞋们能想到的大概也就是 jvm 内存或者 redis 了。因为这个对应关系一般是不会长久存储的,通常在某个热点事件中作为查询。如果是 redis,可以设置过期时间作为驱逐。那么在 jvm 内存中,我们需要考虑到的是 LRU。即最近最常使用:

  1. 使用过的 key 需要放到队列的队首。
  2. 最不经常使用的一旦超过队列限制的长度,需要将其删除。

那么我们需要用哪种数据结构实现这中条件的队列呢?

GET

  1. 假设这个 key 不存在,那么返回 null。
  2. 假设 key 存在,需要返回值的同时,需要将对应的 key 删除,并且将 key 放到队首。

在上述的这种场景下,明显底层是数组的集合如 ArrayList 是不适用的。别说你这想不通哈。。

那就只剩下链表了如 LinkedList,但是 LinedList 查询时需要遍历链表。如果我们在存入 LinkedList 的同时,同样存入 map,那是不是就行了。当然。。。。不是啦,这个 map 有个要求,node 需要保存上一个节点,这样在查到值的同时,获取前一个节点,就可以在链表中删除对应的节点了。

PUT

  1. 假设 key 不存在,放入队首。
  2. 假设 key 存在,删除这个 key,同时放到队首。

经过 Get 的铺垫,这个不用说了吧!最终结果是 LinedHashMap。LinkedHashMap 的具体车辙这边就不逼逼了,还是自己看历史文章吧!

结尾

这边不考虑并发导致的线程不安全哈,只是一个参考~~ 讲了大半天,大家应该还是有些会看不明白的,请下方留言。没办法,语文差啊。



Tags:限流   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,如有任何标注错误或版权侵犯请与我们联系(Email:2595517585@qq.com),我们将及时更正、删除,谢谢。
▌相关推荐
Guava提供的RateLimiter可以限制物理或逻辑资源的被访问速率,咋一听有点像java并发包下的Samephore,但是又不相同,RateLimiter控制的是速率,Samephore控制的是并发量。RateLimit...【详细内容】
2021-09-17  Tags: 限流  点击:(72)  评论:(0)  加入收藏
自适应限流 TCP Vegas netflix-concuurency-limits alpha , beta & threshold 变量queueSize 动态调整函数 平滑递减 smoothingDecrease 自适应限流一般的限流常常需...【详细内容】
2021-08-13  Tags: 限流  点击:(62)  评论:(0)  加入收藏
Nginx来限制访问控制的方法有多种,nginx主要有2个模块控制,但是那些不支持自定义,非常死,在大多数场景下并不实用。今天分享一个:利用openresty+lua+redis 实现封杀频繁恶意访问I...【详细内容】
2021-08-12  Tags: 限流  点击:(119)  评论:(0)  加入收藏
最近,我们的业务系统引入了Guava的RateLimiter限流组件,它是基于令牌桶算法实现的,而令牌桶是非常经典的限流算法。本文将跟大家一起学习几种经典的限流算法。 限流是什么?维...【详细内容】
2021-08-06  Tags: 限流  点击:(86)  评论:(0)  加入收藏
1.安装依赖环境yum -y install wget gcc-c++ ncurses ncurses-devel cmake make perl bison openssl openssl-devel gcc* libxml2 libxml2-devel curl-devel libjpeg* libpn...【详细内容】
2021-08-03  Tags: 限流  点击:(63)  评论:(0)  加入收藏
What is 限流?限流顾名思义,限制流量或者说叫流量管制。很形象的比喻如老式电闸都安装了保险丝,一旦有人使用超大功率的设备,保险丝就会烧断以保护各个电器不被强电流给烧坏。W...【详细内容】
2021-06-28  Tags: 限流  点击:(89)  评论:(0)  加入收藏
引言 在web开发中功能是基石,除了功能以外运维和防护就是重头戏了。因为在网站运行期间可能会因为突然的访问量导致业务异常、也有可能遭受别人恶意攻击 所以我们的接口需要...【详细内容】
2021-05-31  Tags: 限流  点击:(114)  评论:(0)  加入收藏
Sentinel是阿里巴巴开源的限流器熔断器,并且带有可视化操作界面。在日常开发中,限流功能时常被使用,用于对某些接口进行限流熔断,譬如限制单位时间内接口访问次数;或者按照某种规...【详细内容】
2021-04-30  Tags: 限流  点击:(251)  评论:(0)  加入收藏
目前,Spring Cloud Gateway是仅次于Spring Cloud Netflix的第二个最受欢迎的Spring Cloud项目(就GitHub上的星级而言)。它是作为Spring Cloud系列中Zuul代理的继任者而创建的。...【详细内容】
2021-04-21  Tags: 限流  点击:(427)  评论:(0)  加入收藏
前言在一个高并发系统中对流量的把控是非常重要的,当巨大的流量直接请求到我们的服务器上没多久就可能造成接口不可用,不处理的话甚至会造成整个应用不可用。那么何为限流呢?顾...【详细内容】
2020-12-15  Tags: 限流  点击:(121)  评论:(0)  加入收藏
▌简易百科推荐
阿里云镜像源地址及安装网站地址https://developer.aliyun.com/mirror/centos?spm=a2c6h.13651102.0.0.3e221b111kK44P更新源之前把之前的国外的镜像先备份一下 切换到yumcd...【详细内容】
2021-12-27  干程序那些事    Tags:CentOS7镜像   点击:(1)  评论:(0)  加入收藏
前言在实现TCP长连接功能中,客户端断线重连是一个很常见的问题,当我们使用netty实现断线重连时,是否考虑过如下几个问题: 如何监听到客户端和服务端连接断开 ? 如何实现断线后重...【详细内容】
2021-12-24  程序猿阿嘴  CSDN  Tags:Netty   点击:(12)  评论:(0)  加入收藏
一. 配置yum源在目录 /etc/yum.repos.d/ 下新建文件 google-chrome.repovim /etc/yum.repos.d/google-chrome.repo按i进入编辑模式写入如下内容:[google-chrome]name=googl...【详细内容】
2021-12-23  有云转晴    Tags:chrome   点击:(7)  评论:(0)  加入收藏
一. HTTP gzip压缩,概述 request header中声明Accept-Encoding : gzip,告知服务器客户端接受gzip的数据 response body,同时加入以下header:Content-Encoding: gzip:表明bo...【详细内容】
2021-12-22  java乐园    Tags:gzip压缩   点击:(9)  评论:(0)  加入收藏
yum -y install gcc automake autoconf libtool makeadduser testpasswd testmkdir /tmp/exploitln -s /usr/bin/ping /tmp/exploit/targetexec 3< /tmp/exploit/targetls -...【详细内容】
2021-12-22  SofM    Tags:Centos7   点击:(7)  评论:(0)  加入收藏
Windows操作系统和Linux操作系统有何区别?Windows操作系统:需支付版权费用,(华为云已购买正版版权,在华为云购买云服务器的用户安装系统时无需额外付费),界面化的操作系统对用户使...【详细内容】
2021-12-21  卷毛琴姨    Tags:云服务器   点击:(6)  评论:(0)  加入收藏
参考资料:Hive3.1.2安装指南_厦大数据库实验室博客Hive学习(一) 安装 环境:CentOS 7 + Hadoop3.2 + Hive3.1 - 一个人、一座城 - 博客园1.安装hive1.1下载地址hive镜像路径 ht...【详细内容】
2021-12-20  zebra-08    Tags:Hive   点击:(9)  评论:(0)  加入收藏
以下是服务器安全加固的步骤,本文以腾讯云的CentOS7.7版本为例来介绍,如果你使用的是秘钥登录服务器1-5步骤可以跳过。1、设置复杂密码服务器设置大写、小写、特殊字符、数字...【详细内容】
2021-12-20  网安人    Tags:服务器   点击:(7)  评论:(0)  加入收藏
项目中,遇到了一个问题,就是PDF等文档不能够在线预览,预览时会报错。错误描述浏览器的console中,显示如下错误:nginx代理服务报Mixed Content: The page at ******** was loaded...【详细内容】
2021-12-17  mdong    Tags:Nginx   点击:(7)  评论:(0)  加入收藏
转自: https://kermsite.com/p/wt-ssh/由于格式问题,部分链接、表格可能会失效,若失效请访问原文密码登录 以及 通过密钥实现免密码登录Dec 15, 2021阅读时长: 6 分钟简介Windo...【详细内容】
2021-12-17  LaLiLi    Tags:SSH连接   点击:(16)  评论:(0)  加入收藏
最新更新
栏目热门
栏目头条