首页 > 其他分享 >04高可用高并发(D1_高并发 - D2_限流)

04高可用高并发(D1_高并发 - D2_限流)

时间:2024-11-19 15:43:06浏览次数:3  
标签:令牌 RateLimiter 04 acquire 并发 限流 limiter 速率

目录

学习前言

一、限流简介

1. 算法

2. 分类

2.1. 应用级 - 单机

2.2. 分布式

二、限流方案

方案一:令牌桶方式(Token Bucket)

举例:Guava RateLimiter - 平滑突发限流(SmoothBursty)

举例:Guava RateLimiter - SmoothWarmingUp

方案二:漏桶方式

令牌桶和漏桶对比

方案三:计数器方式

采用AtomicInteger

采用令牌Semaphore

采用ThreadPoolExecutor java线程池

三、压力测试


学习前言

每个系统都有服务的上线,所以当流量超过服务极限能力时,系统可能会出现卡死、崩溃的情况,所以就有了降

级和限流。限流其实就是:当高并发或者瞬时高并发时,为了保证系统的稳定性、可用性,系统以牺牲部分请求

为代价或者延迟处理请求为代价,保证系统整体服务可用。

一、限流简介

每个系统都有服务的上线,所以当流量超过服务极限能力时,系统可能会出现卡死、崩溃的情况,所以就有了降级和限流。限流其实就

是:当高并发或者瞬时高并发时,为了保证系统的稳定性、可用性,系统以牺牲部分请求为代价或者延迟处理请求为代价,保证系统整体

服务可用。

1. 算法

令牌桶(Token Bucket)、漏桶(leaky bucket)和计数器算法是最常用的三种限流的算法。

2. 分类

2.1. 应用级 - 单机

应用级限流方式只是单应用内的请求限流,不能进行全局限流。

  1. 限流总资源数
  2. 限流总并发/连接/请求数
  3. 限流某个接口的总并发/请求数
  4. 限流某个接口的时间窗请求数
  5. 平滑限流某个接口的请求数
  6. Guava RateLimiter

2.2. 分布式

我们需要分布式限流接入层限流来进行全局限流。

  1. redis+lua实现中的lua脚本
  2. 使用Nginx+Lua实现的Lua脚本
  3. 使用 OpenResty 开源的限流方案
  4. 限流框架,比如Sentinel实现降级限流熔断

二、限流方案

方案一:令牌桶方式(Token Bucket)

令牌桶算法是网络流量整形(Traffic Shaping)和速率限制(Rate Limiting)中最常使用的一种算法。先有一个木桶,系统按照固定

速度,往桶里加入Token,如果桶已经满了就不再添加。当有请求到来时,会各自拿走一个Token,取到Token 才能继续进行请求处

理,没有Token 就拒绝服务。

这里如果一段时间没有请求时,桶内就会积累一些Token,下次一旦有突发流量,只要Token足够,也能一次处理,所以令牌桶算法的特

点是允许突发流量

举例:Guava RateLimiter - 平滑突发限流(SmoothBursty)

Guava RateLimiter提供了令牌桶算法实现:平滑突发限流(SmoothBursty)和平滑预热限流(SmoothWarmingUp)实现。

  • Case 1
RateLimiter limiter = RateLimiter.create(5);
System.out.println(limiter.acquire());
System.out.println(limiter.acquire());
System.out.println(limiter.acquire());
System.out.println(limiter.acquire());
System.out.println(limiter.acquire());
System.out.println(limiter.acquire());

// 将得到类似如下的输出:
0.0
0.198239
0.196083
0.200609
0.199599
0.19961

1、RateLimiter.create(5)表示桶容量为5且每秒新增5个令牌,即每隔200毫秒新增一个令牌;

2、limiter.acquire()表示消费一个令牌,如果当前桶中有足够令牌则成功(返回值为0),如果桶中没有令牌则暂停一段时间,比如发令

牌间隔是200毫秒,则等待200毫秒后再去消费令牌(如上测试用例返回的为0.198239,差不多等待了200毫秒桶中才有令牌可用),这

种实现将突发请求速率平均为了固定请求速率。如果结构不想等待可以采用tryAcquire立刻返回!

  • Case 2 - RateLimiter的突发情况处理:
RateLimiter limiter = RateLimiter.create(5);
System.out.println(limiter.acquire(5));
System.out.println(limiter.acquire(1));
System.out.println(limiter.acquire(1))

// 将得到类似如下的输出:
0.0
0.98745
0.183553
0.199909

limiter.acquire(5)表示桶的容量为5且每秒新增5个令牌,令牌桶算法允许一定程度的突发,所以可以一次性消费5个令牌,但接下来的

limiter.acquire(1)将等待差不多1秒桶中才能有令牌,且接下来的请求也整形为固定速率了。

  • Case 3 - RateLimiter的突发情况处理:
RateLimiter limiter = RateLimiter.create(5);
System.out.println(limiter.acquire(10));
System.out.println(limiter.acquire(1));
System.out.println(limiter.acquire(1));

// 将得到类似如下的输出:
0.0
1.997428
0.192273
0.200616

同上边的例子类似,第一秒突发了10个请求,令牌桶算法也允许了这种突发(允许消费未来的令牌),但接下来的limiter.acquire(1)将等

待差不多2秒桶中才能有令牌,且接下来的请求也整形为固定速率了。

  • Case 4
RateLimiter limiter = RateLimiter.create(2);
System.out.println(limiter.acquire());
Thread.sleep(2000L);
System.out.println(limiter.acquire());
System.out.println(limiter.acquire());
System.out.println(limiter.acquire());
System.out.println(limiter.acquire());
System.out.println(limiter.acquire());

// 将得到类似如下的输出:
0.0
0.0
0.0
0.0
0.499876
0.495799

1、创建了一个桶容量为2且每秒新增2个令牌;

2、首先调用limiter.acquire()消费一个令牌,此时令牌桶可以满足(返回值为0);

3、然后线程暂停2秒,接下来的两个limiter.acquire()都能消费到令牌,第三个limiter.acquire()也同样消费到了令牌,到第四个时就需要

等待500毫秒了。

此处可以看到我们设置的桶容量为2(即允许的突发量),这是因为SmoothBursty中有一个参数:最大突发秒数(maxBurstSeconds)

默认值是1s,突发量/桶容量=速率*maxBurstSeconds,所以本示例桶容量/突发量为2,例子中前两个是消费了之前积攒的突发量,而第

三个开始就是正常计算的了。令牌桶算法允许将一段时间内没有消费的令牌暂存到令牌桶中,留待未来使用,并允许未来请求的这种突

发.

SmoothBursty通过平均速率和最后一次新增令牌的时间计算出下次新增令牌的时间的,另外需要一个桶暂存一段时间内没有使用的令牌

(即可以突发的令牌数)。另外RateLimiter还提供了tryAcquire方法来进行无阻塞或可超时的令牌消费。

因为SmoothBursty允许一定程度的突发,会有人担心如果允许这种突发,假设突然间来了很大的流量,那么系统很可能扛不住这种突

发。因此需要一种平滑速率的限流工具,从而系统冷启动后慢慢的趋于平均固定速率(即刚开始速率小一些,然后慢慢趋于我们设置的固

定速率)。Guava也提供了SmoothWarmingUp来实现这种需求类似漏桶算法;

举例:Guava RateLimiter - SmoothWarmingUp

SmoothWarmingUp创建方式:RateLimiter.create(doublepermitsPerSecond, long warmupPeriod, TimeUnit unit)

permitsPerSecond表示每秒新增的令牌数,warmupPeriod表示在从冷启动速率过渡到平均速率的时间间隔。

RateLimiter limiter = RateLimiter.create(5,1000, TimeUnit.MILLISECONDS);
for(inti =1; i < 5;i++) {
    System.out.println(limiter.acquire());
}
Thread.sleep(1000L);
for(inti =1; i < 5;i++) {
    System.out.println(limiter.acquire());
}

// 将得到类似如下的输出:
0.0
0.51767
0.357814
0.219992
0.199984
0.0
0.360826
0.220166
0.199723
0.199555

速率是梯形上升速率的,也就是说冷启动时会以一个比较大的速率慢慢到平均速率;然后趋于平均速率(梯形下降到平均速率)。可以通

过调节warmupPeriod参数实现一开始就是平滑固定速率。

方案二:漏桶方式

水(请求)先进入到漏桶里,漏桶以一定的速度出水(接口有响应速率),当水流入速度过大会直接溢出(访问频率超过接口响应速率),然后就拒绝

请求,可以看出漏桶算法能强行限制数据的传输速率。

可见这里有两个变量,一个是桶的大小,支持流量突发增多时可以存多少的水(burst),另一个是水桶漏洞的大小(rate)。

因为漏桶的漏出速率是固定的参数,所以,即使网络中不存在资源冲突(没有发生拥塞),漏桶算法也不能使流突发(burst)到端口速率.因此,漏桶

算法对于存在突发特性的流量来说缺乏效率.

令牌桶和漏桶对比

  • 令牌桶是按照固定速率往桶中添加令牌,请求是否被处理需要看桶中令牌是否足够,当令牌数减为零时则拒绝新的请求;
  • 漏桶则是按照常量固定速率流出请求,流入请求速率任意,当流入的请求数累积到漏桶容量时,则新流入的请求被拒绝;
  • 令牌桶限制的是平均流入速率(允许突发请求,只要有令牌就可以处理,支持一次拿3个令牌,4个令牌),并允许一定程度突发流量;
  • 漏桶限制的是常量流出速率(即流出速率是一个固定常量值,比如都是1的速率流出,而不能一次是1,下次又是2),从而平滑突发流入速率;
  • 令牌桶允许一定程度的突发,而漏桶主要目的是平滑流入速率;
  • 两个算法实现可以一样,但是方向是相反的,对于相同的参数得到的限流效果是一样的。

方案三:计数器方式

计数器限流算法也是比较常用的,主要用来限制总并发数,比如数据库连接池大小、线程池大小、程序访问并发数等都是使用计数器算

法。也是最简单粗暴的算法。

采用AtomicInteger

使用AomicInteger来进行统计当前正在并发执行的次数,如果超过域值就简单粗暴的直接响应给用户,说明系统繁忙,请稍后再试或其它

跟业务相关的信息。

弊端:使用 AomicInteger 简单粗暴超过域值就拒绝请求,可能只是瞬时的请求量高,也会拒绝请求。

采用令牌Semaphore

使用Semaphore信号量来控制并发执行的次数,如果超过域值信号量,则进入阻塞队列中排队等待获取信号量进行执行。

如果阻塞队列中排队的请求过多超出系统处理能力,则可以在拒绝请求。

相对Atomic优点:如果是瞬时的高并发,可以使请求在阻塞队列中排队,而不是马上拒绝请求,从而达到一个流量削峰的目的。

采用ThreadPoolExecutor java线程池

固定线程池大小,超出固定先线程池和最大的线程数,拒绝线程请求;

三、压力测试

给个思路

  • Linux AB

可以参考Linux - ab压力测试

  • 写代码

比如:

@SneakyThrows
public static void test(int clientSize) {
    CountDownLatch downLatch = new CountDownLatch(clientSize);
    ExecutorService fixedThreadPool = Executors.newFixedThreadPool(clientSize);
    IntStream.range(0, clientSize).forEach(i ->
            fixedThreadPool.submit(() -> {
                RestTemplate restTemplate = new RestTemplate();
                restTemplate.getForObject("http://localhost:8080/limit1", ResponseResult.class);
                downLatch.countDown();
            })
    );
    downLatch.await();
    fixedThreadPool.shutdown();
}
  • 其它测试工具,LoadRunner,Jmeter...

标签:令牌,RateLimiter,04,acquire,并发,限流,limiter,速率
From: https://blog.csdn.net/qq_51226710/article/details/143887537

相关文章

  • 04高可用高并发(D1_高并发 - D3_降级 - 熔断)
    目录学习前言一、为什么会有这个话题二、基本的容错模式三、服务降级1.降级服务的特征2.降级方式3.降级预案4.服务降级分类5.自动降级分类6.服务降级需考虑的问题四、服务熔断1.相关概念2.熔断流程2.1.基本的断路器模式2.2.扩展的断路器模式2.3.......
  • 如何在Ubuntu 20.04 LTS上安装Dotnet Core?
    在本教程中,我们将向您展示如何在香港服务器的Ubuntu20.04LTS系统上安装DotnetCore。.NETCore是一个免费的开源软件框架和开源软件框架。它是由Microsoft开发的。它是由Microsoft开发的。.NETCore是一个非常强大的框架。它通常用于开发Web应用程序。步骤1.首先,通过apt在......
  • WasomCodeX试用---Ubuntu20.04系统
    安装WasomeIDE下载安装包并解压可获得如下文件内容:/WasomeIDE$lscode_amd64.debiecc.img.tarinstall.shwebide.vsixheadersinstall_docker.shmoduleswebview-toolkit-ui.tar执行install.sh文件如果系统未安装vscode,则在执行install.sh时会......
  • 命名空间、STL、Lambda表达式与并发编程
        在深入学习C++的过程中,了解并掌握进阶特性对于编写高效、灵活的程序至关重要。    本篇博客将详细介绍C++中的命名空间、标准模板库(STL)、lambda表达式、move语义及并发编程,帮助你更好地驾驭C++语言。1.命名空间(Namespace)    命名空间用于组织代码......
  • 并发编程体系概述
    作者:京东自有品牌周振类别定义特点应用场景Java中的使用进程(Process)计算机程序在操作系统中执行的实例-独立性强、拥有独立的内存空间、创建和销毁开销大-进程间通信复杂-独立的应用程序-高隔离性任务,如数据库服务器-Java应用程序运行在JVM进程中-通过Pr......
  • 如何控制java虚拟线程的并发度?
    jdk21中的虚拟线程已经推出好一段时间了,确实很轻量,先来一段示例:假如有一段提交订单的业务代码:1publicvoidsubmitOrder(IntegerorderId){2sleep(1000);3System.out.println("order:"+orderId+"issubmitted");4}ViewCode这里我们......
  • 20222304 2024-2025-1 《网络与系统攻防技术》实验六实验报告
    1.实验内容1.1实验要求(1)掌握metasploit、nmap的用法。(2)学习前期渗透的方法。(3)利用4个漏洞,实现对靶机的攻击。1.2学习内容(1)metasploit的用法:可以简单总结为“Search-Use-Show-Set-Exploit/run”。(2)四种漏洞的原理。a.Vsftpd后门漏洞:在某些特定版本的vsftpd服务......
  • 04-转录组下游分析-标准化、聚类、差异分析
    准备工作1.数据标准化标准化前需要进行数据预处理过滤低表达的基因,并检查是否有异常样本以下是常见的几种过滤方式(过滤的标准都可以自己调整)1:在至少在75%的样本中都表达的基因(表达是指在某个样本中count值>0)2:过滤平均值count<10的基因3:过滤平均cpm<10的基因为什么......
  • 打卡信奥刷题(262)用C++信奥P2004[普及组/提高] 领地选择
    领地选择题目描述作为在虚拟世界里统帅千军万马的领袖,小Z认为天时、地利、人和三者是缺一不可的,所以,谨慎地选择首都的位置对于小Z来说是非常重要的。首都被认为是一个占地C×......
  • 20222404 2024-2025-1 《网络与系统攻防技术》实验六实验报告
    1.实验内容1.1实验要求掌握metasploit的用法。(1)前期渗透①主机发现(可用Aux中的arp_sweep,search一下就可以use)②端口扫描:可以直接用nmap,也可以用Aux中的portscan/tcp等。③选做:也可以扫系统版本、漏洞等。(2)Vsftpd源码包后门漏洞(21端口)1.2本周学习内容说实话印象最深的就是......