文章详情

短信预约-IT技能 免费直播动态提醒

请输入下面的图形验证码

提交验证

短信预约提醒成功

不会高并发系统限流,肯定进不了大厂!

2024-12-24 20:08

关注

[[315529]]

图片来自 Pexel

缓存

缓存比较好理解,在大型高并发系统中,如果没有缓存数据库将分分钟被爆,系统也会瞬间瘫痪。

使用缓存不单单能够提升系统访问速度、提高并发访问量,也是保护数据库、保护系统的有效方式。大型网站一般主要是“读”,缓存的使用很容易被想到。

在大型“写”系统中,缓存也常常扮演着非常重要的角色。比如累积一些数据批量写入,内存里面的缓存队列(生产消费),以及 HBase 写数据的机制等等也都是通过缓存提升系统的吞吐量或者实现系统的保护措施。

甚至消息中间件,你也可以认为是一种分布式的数据缓存。

降级

服务降级是当服务器压力剧增的情况下,根据当前业务情况及流量对一些服务和页面有策略的降级,以此释放服务器资源以保证核心任务的正常运行。

降级往往会指定不同的级别,面临不同的异常等级执行不同的处理。根据服务方式:可以拒接服务,可以延迟服务,也有时候可以随机服务。

根据服务范围:可以砍掉某个功能,也可以砍掉某些模块。总之服务降级需要根据不同的业务需求采用不同的降级策略。主要的目的就是服务虽然有损但是总比没有好。

限流

限流可以认为是服务降级的一种,限流就是限制系统的输入和输出流量以达到保护系统的目的。

一般来说,系统的吞吐量是可以被测算的,为了保证系统的稳定运行,一旦达到需要限制的阈值,就需要限制流量并采取一些措施以完成限制流量的目的。

比如:延迟处理,拒绝处理,或者部分拒绝处理等等。

限流的算法

常见的限流算法有:计数器、漏桶和令牌桶算法。

计数器

计数器是最简单粗暴的算法。比如某个服务最多只能每秒钟处理 100 个请求。

我们可以设置一个 1 秒钟的滑动窗口,窗口中有 10 个格子,每个格子 100 毫秒,每 100 毫秒移动一次,每次移动都需要记录当前服务请求的次数。

内存中需要保存 10 次的次数,可以用数据结构 LinkedList 来实现。格子每次移动的时候判断一次,当前访问次数和 LinkedList 中最后一个相差是否超过 100,如果超过就需要限流了。

很明显,当滑动窗口的格子划分的越多,那么滑动窗口的滚动就越平滑,限流的统计就会越精确。

示例代码如下:

  1. //服务访问次数,可以放在Redis中,实现分布式系统的访问计数 
  2. Long counter = 0L; 
  3. //使用LinkedList来记录滑动窗口的10个格子。 
  4. LinkedList ll = new LinkedList(); 
  5.  
  6. public static void main(String[] args) 
  7.     Counter counter = new Counter(); 
  8.  
  9.     counter.doCheck(); 
  10.  
  11. private void doCheck() 
  12.     while (true
  13.     { 
  14.         ll.addLast(counter); 
  15.  
  16.         if (ll.size() > 10) 
  17.         { 
  18.             ll.removeFirst(); 
  19.         } 
  20.  
  21.         //比较最后一个和第一个,两者相差一秒 
  22.         if ((ll.peekLast() - ll.peekFirst()) > 100) 
  23.         { 
  24.             //To limit rate 
  25.         } 
  26.  
  27.         Thread.sleep(100); 
  28.     } 

漏桶算法

漏桶算法即 leaky bucket 是一种非常常用的限流算法,可以用来实现流量整形(Traffic Shaping)和流量控制(Traffic Policing)。

贴了一张维基百科上示意图帮助大家理解:

漏桶算法的主要概念如下:

漏桶算法比较好实现,在单机系统中可以使用队列来实现(.Net 中 TPL DataFlow 可以较好的处理类似的问题,你可以在这里找到相关的介绍),在分布式环境中消息中间件或者 Redis 都是可选的方案。

令牌桶算法

令牌桶算法是一个存放固定容量令牌(token)的桶,按照固定速率往桶里添加令牌。

令牌桶算法基本可以用下面的几个概念来描述:

如下图:

令牌算法是根据放令牌的速率去控制输出的速率,也就是上图的 to network 的速率。to network 我们可以理解为消息的处理程序,执行某段业务或者调用某个 RPC。

漏桶和令牌桶的比较

令牌桶可以在运行时控制和调整数据处理的速率,处理某时的突发流量。

放令牌的频率增加可以提升整体数据处理的速度,而通过每次获取令牌的个数增加或者放慢令牌的发放速度可以降低整体数据处理速度。

而漏桶不行,因为它的流出速率是固定的,程序处理速度也是固定的。整体而言,令牌桶算法更优,但是实现更为复杂一些。

限流算法实现

Guava

Guava 是一个 Google 开源项目,包含了若干被 Google 的 Java 项目广泛依赖的核心库,其中的 RateLimiter 提供了令牌桶算法实现:平滑突发限流(SmoothBursty)和平滑预热限流(SmoothWarmingUp)实现。

①常规速率:创建一个限流器,设置每秒放置的令牌数:2 个。

返回的 RateLimiter 对象可以保证 1 秒内不会给超过 2 个令牌,并且是固定速率的放置。达到平滑输出的效果:

  1. public void test() 
  2.      
  3.     RateLimiter r = RateLimiter.create(2); 
  4.  
  5.     while (true
  6.     { 
  7.          
  8.         System.out.println(r.acquire()); 
  9.     } 

上面代码执行的结果如下图,基本是 0.5 秒一个数据。拿到令牌后才能处理数据,达到输出数据或者调用接口的平滑效果。

acquire() 的返回值是等待令牌的时间,如果需要对某些突发的流量进行处理的话,可以对这个返回值设置一个阈值,根据不同的情况进行处理,比如过期丢弃。

②突发流量:突发流量可以是突发的多,也可以是突发的少。首先来看个突发多的例子。还是上面例子的流量,每秒 2 个数据令牌。

如下代码使用 acquire 方法,指定参数:

  1. System.out.println(r.acquire(2));  
  2. System.out.println(r.acquire(1));  
  3. System.out.println(r.acquire(1));  
  4. System.out.println(r.acquire(1)); 

得到如下类似的输出。

如果要一次新处理更多的数据,则需要更多的令牌。代码首先获取 2 个令牌,那么下一个令牌就不是 0.5 秒之后获得了,还是 1 秒以后,之后又恢复常规速度。

这是一个突发多的例子,如果是突发没有流量,如下代码:

  1. System.out.println(r.acquire(1));  
  2. Thread.sleep(2000);  
  3. System.out.println(r.acquire(1));  
  4. System.out.println(r.acquire(1));  
  5. System.out.println(r.acquire(1));  

得到如下类似的结果:

等了两秒钟之后,令牌桶里面就积累了 3 个令牌,可以连续不花时间的获取出来。处理突发其实也就是在单位时间内输出恒定。

这两种方式都是使用的 RateLimiter 的子类 SmoothBursty。另一个子类是 SmoothWarmingUp,它提供的有一定缓冲的流量输出方案。

  1.  
  2. RateLimiter r = RateLimiter.create(2,3,TimeUnit.SECONDS); 
  3.  
  4. while (true) { 
  5.      
  6.     System.out.println(r.acquire(1)); 
  7.     System.out.println(r.acquire(1)); 
  8.     System.out.println(r.acquire(1)); 
  9.     System.out.println(r.acquire(1)); 

输出结果如下图,由于设置了缓冲的时间是 3 秒,令牌桶一开始并不会 0.5 秒给一个消息,而是形成一个平滑线性下降的坡度,频率越来越高,在 3 秒钟之内达到原本设置的频率,以后就以固定的频率输出。

图中红线圈出来的 3 次累加起来正好是 3 秒左右。这种功能适合系统刚启动需要一点时间来“热身”的场景。

Nginx

对于 Nginx 接入层限流可以使用 Nginx 自带的两个模块:

①ngx_http_limit_conn_module

我们经常会遇到这种情况,服务器流量异常,负载过大等等。对于大流量恶意的攻击访问,会带来带宽的浪费,服务器压力,影响业务,往往考虑对同一个 IP 的连接数,并发数进行限制。

ngx_http_limit_conn_module 模块来实现该需求。该模块可以根据定义的键来限制每个键值的连接数,如同一个 IP 来源的连接数。

并不是所有的连接都会被该模块计数,只有那些正在被处理的请求(这些请求的头信息已被完全读入)所在的连接才会被计数。

我们可以在 nginx_conf 的 http{} 中加上如下配置实现限制:

  1. #限制每个用户的并发连接数,取名one 
  2. limit_conn_zone $binary_remote_addr zone=one:10m; 
  3.  
  4. #配置记录被限流后的日志级别,默认error级别 
  5. limit_conn_log_level error; 
  6. #配置被限流后返回的状态码,默认返回503 
  7. limit_conn_status 503; 

然后在 server{} 里加上如下代码:

  1. #限制用户并发连接数为1 
  2. limit_conn one 1; 

然后我们是使用 ab 测试来模拟并发请求:

ab -n 5 -c 5 http://10.23.22.239/index.html

得到下面的结果,很明显并发被限制住了,超过阈值的都显示 503:

另外刚才是配置针对单个 IP 的并发限制,还是可以针对域名进行并发限制,配置和客户端 IP 类似。

  1. #http{}段配置 
  2. limit_conn_zone $ server_name zone=perserver:10m; 
  3. #server{}段配置 
  4. limit_conn perserver 1; 

②ngx_http_limit_req_module

上面我们使用到了 ngx_http_limit_conn_module 模块,来限制连接数。那么请求数的限制该怎么做呢?

这就需要通过 ngx_http_limit_req_module 模块来实现,该模块可以通过定义的键值来限制请求处理的频率。

特别的,可以限制来自单个 IP 地址的请求处理频率。限制的方法是使用了漏斗算法,每秒固定处理请求数,推迟过多请求。

如果请求的频率超过了限制域配置的值,请求处理会被延迟或被丢弃,所以所有的请求都是以定义的频率被处理的。

在 http{} 中配置,#区域名称为 one,大小为 10m,平均处理的请求频率不能超过每秒一次。

limit_req_zone $binary_remote_addr zone=one:10m rate=1r/s;

在 server{} 中配置:

  1. 设置每个IP桶的数量为5 
  2. limit_req zone=one burst=5; 

上面设置定义了每个 IP 的请求处理只能限制在每秒 1 个。并且服务端可以为每个 IP 缓存 5 个请求,如果操作了 5 个请求,请求就会被丢弃。

使用 ab 测试模拟客户端连续访问 10 次:

ab -n 10 -c 10 http://10.23.22.239/index.html

如下图,设置了桶的个数为 5 个。一共 10 个请求,第一个请求马上被处理。第 2-6 个被存放在桶中。

由于桶满了,没有设置 nodelay 因此,余下的 4 个请求被丢弃:

 

来源:cnblogs.com/haoxinyue/p/679230内容投诉

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

软考中级精品资料免费领

  • 历年真题答案解析
  • 备考技巧名师总结
  • 高频考点精准押题
  • 2024年上半年信息系统项目管理师第二批次真题及答案解析(完整版)

    难度     813人已做
    查看
  • 【考后总结】2024年5月26日信息系统项目管理师第2批次考情分析

    难度     354人已做
    查看
  • 【考后总结】2024年5月25日信息系统项目管理师第1批次考情分析

    难度     318人已做
    查看
  • 2024年上半年软考高项第一、二批次真题考点汇总(完整版)

    难度     435人已做
    查看
  • 2024年上半年系统架构设计师考试综合知识真题

    难度     224人已做
    查看

相关文章

发现更多好内容

猜你喜欢

AI推送时光机
位置:首页-资讯-后端开发
咦!没有更多了?去看看其它编程学习网 内容吧
首页课程
资料下载
问答资讯