- 👏作者简介:大家好,我是爱吃芝士的土豆倪,24届校招生Java选手,很高兴认识大家
- 📕系列专栏:Spring源码、JUC源码、Kafka原理、分布式技术原理
- 🔥如果感觉博主的文章还不错的话,请👍三连支持👍一下博主哦
- 🍂博主正在努力完成2023计划中:源码溯源,一探究竟
- 📝联系方式:nhs19990716,加我进群,大家一起学习,一起进步,一起对抗互联网寒冬👀
文章目录
- 限流的作用
- 限流的算法
- 计数器
- 滑动窗口
- 漏桶
- 令牌桶
- 限流的实现
- Sentinel
- 服务熔断
- demo
- Sentinel中的流量控制
对于后端来说,我们需要提供一些接口去进行交互,比如登陆注册,支付下单等这样的一些功能,所以我们需要搭建一个整体的架构,早期的话可能没那么复杂,就是通过ssm框架组成的架构,然后通过部署tomcat来提供一个端口,随着整个并发量上去之后,我们需要去提供高性能的服务。
当我们的客户端,用户量的访问大了之后,对于后端系统的并发量会更高,并发量越高对于后端整个处理的能力就是一个挑战,因为我们需要保证,用户从10个到10000个的时候,我们需要提供给用户的是 它的响应时间不能超过多少。因为用户的操作是存在一个心理预期的,这就是所谓的用户体验。其次就是系统是否能够支撑这么大的量,是否会挂掉,挂掉之后网站无法提供对外服务的情况下,你无法产生商业价值。
比如说双十一零点的时候,那一瞬间瞬时的流量一定会大于正常时间的吞吐量,所以需要采用一定的机制来限流
限流的作用
- 保护系统避免被瞬时流量冲垮
- 预防恶意请求(如果自己公司不提供安全的话,可以去买高防的服务器)
- 针对请求进行限制
服务器能支撑的连接数是多少
接口的处理能力(QPS/TPS),可以使用Jmeter来测试平均响应时间
资源限制(cpu(线程池)、内存、网络资源)
- 如何控制流量
限流的指标(可以容纳的流量、已经容纳的流量、可以接受的流量) 阈值(基于这个指标)
限流的过程(通过算法来实现)
限流的结果(处理策略)
限流的算法
计数器
(Zookeeper:RequestThrottle 限流阀)、线程池大小、连接数大小
滑动窗口
实际上发送方和接收方都维护了一个滑动窗口
当发送端发送了一个数据包,等到接收端接收到后,接收端窗口开始滑动,发送端需要等待返回后才能滑动
其限流的核心就是流量只能在这个窗口里面,但是在tcp里面,其窗口是可以灵活的扩大的,其会根据当前网络拥挤的程度来决定窗口大小。
最大只能发送五个(阈值),超过了就不能发送了
所以说io通信是一个阻塞通信其实就是这样,基于数据包处理的结果,等到这个数据返回,如果数据一直不返回,那么这个时候请求是阻塞的。
漏桶
(用来控制传输速率的)本质上控制的是发送者的速度
流入水滴的速率代表的是请求,而滴出水滴的速度代表是处理的请求,不管来的请求有多少,但是能够处理的请求就这么多。
漏桶算法的特点:
- 水的流出速度是固定的
- 桶的大小也是固定的
令牌桶
其恒定的生成速率决定了并发数,假如说我每秒生成10个令牌,那么我的qps 就是 10
令牌桶对比漏桶的区别是,其能够处理瞬时突发流量,而不像漏桶一样,流出的速度是固定的。
令牌桶的设计:
-
桶的大小
-
令牌标记
-
定时任务生成令牌
-
提供令牌获取的接口
限流的实现
Semphore 信号量
分布式限流
其大小怎么计算呢?通过压测来进行计算
//单机实现
//令牌桶算法
RateLimiter rateLimiter=RateLimiter.create(10); //TPS=10
public void doRequest(){
if(rateLimiter.tryAcquire()){ //获取令牌
System.out.println("success");
}else{
System.out.println("failed");
}
}
// 令牌桶不需要释放,处理完后自动丢弃
Sentinel
Sentinel 是阿里中间件团队开源的,面向分布式服务架构的轻量级高可用流量控制组件,主要以流量为切入点,从流量控制、熔断降级、系统负载保护等多个维度来帮助用户保护服务的稳定性。
其中对于Sentinel最重要的两个东西:
- 资源(需要被保护的东西)
- 规则(限流的规则/熔断规则)
所有的流量经过这个组件后,会根据这个规则去控制这个流量来进行处理。
而熔断是一个这样的概念:
上图存在很多相互调用的情况,里面存在很多服务调用的链路,当Service D出现故障,会导致G F阻塞,间接的又会导致A B阻塞,如果特别多的请求阻塞在这里之后,会占用特别多的资源,内存、服务器、cpu的资源,这时候会导致服务器因为大量的资源被占用而导致其他问题。
当出现这种情况的时候,会触发一种熔断的方式,熔断以后会触发降级。
服务熔断
Sentinel的服务熔断有两种方式:基于响应时间的熔断和基于异常比率的熔断。基于响应时间的熔断是根据服务的响应时间来判断是否需要熔断,当服务的响应时间超过设定的阈值时,触发熔断。基于异常比率的熔断是根据服务的异常比率来判断是否需要熔断,当服务的异常比率超过设定的阈值时,触发熔断。这两种方式可以根据具体的业务场景和需求来选择使用。
demo
public static void main(String[] arg) {
initFlowRule(); //初始化限流规则
while(true){
//ResourceName表示资源,控制访问流量的点
/*try(Entry entry=SphU.entry("helloWorld")){
System.out.println("hello world");
}catch (BlockException e){
System.out.println("被拒绝");
}*/
if (SphO.entry("helloWorld")) {
System.out.println("Hello World");
SphO.exit();// 释放
}
}
}
private static void initFlowRule(){
List<FlowRule> rules=new ArrayList<>();
FlowRule flowRule=new FlowRule();
flowRule.setResource("helloWorld"); //针对那个资源设置规则
flowRule.setGrade(RuleConstant.FLOW_GRADE_QPS);//QPS或者并发数
flowRule.setCount(5); //QPS=5
rules.add(flowRule);
FlowRuleManager.loadRules(rules);
}
Demo 运行之后,我们可以在日志 ~/logs/csp/${appName}-metrics.log.xxx 里看到下面的输出:`
-timestamp- -date time - -resource- 5表示, 通过的请求, block: 被拒绝的请
求 ,
1600608724000|2023-09-20 21:32:04|helloWorld|5|6078|5|0|5|0|0|0
1600608725000|2023-09-20 21:32:05|helloWorld|5|32105|5|0|0|0|0|0
1600608726000|2023-09-20 21:32:06|helloWorld|5|41084|5|0|0|0|0|0
1600608727000|2023-09-20 21:32:07|helloWorld|5|72211|5|0|0|0|0|0
1600608728000|2023-09-20 21:32:08|helloWorld|5|60828|5|0|0|0|0|0
1600608729000|2023-09-20 21:32:09|helloWorld|5|41696|5|0|0|0|0|0
@RestController
public class SentinelController {
@Autowired
TestService testService;
@GetMapping("/hello/{name}")
public String sayHello(@PathVariable("name") String name){
return testService.doTest(name);
}
}
@Service
public class TestService {
@SentinelResource(value = "doTest",blockHandler ="blockHandler",fallback = "fallback") //声明限流的资源
public String doTest(String name){
return "hello , "+name;
}
public String blockHandler(String name, BlockException e){ //降级,限流触发的
return "被限流了";
}
// 降级和限流是不一样的,限流可以触发降级,降级是因为已经被触发了
// 降级是第三方业务调用的时候,针对下游的一个返回
// 限流是针对当前服务访问的限制
// 这两个配置只会调用一个
public String fallback(String name){ //降级,熔断触发的
return "被降级了";
}
}
@SpringBootApplication
public class SpringbootSentinelApplication {
public static void main(String[] args) {
initFlowRule();
SpringApplication.run(SpringbootSentinelApplication.class, args);
}
private static void initFlowRule(){
List<FlowRule> rules=new ArrayList<>();
FlowRule flowRule=new FlowRule();
flowRule.setResource("doTest"); //针对那个资源设置规则
flowRule.setGrade(RuleConstant.FLOW_GRADE_QPS);//QPS或者并发数
flowRule.setCount(5); //QPS=5
rules.add(flowRule);
FlowRuleManager.loadRules(rules);
}
}
Sentinel中的流量控制
维度 + 规则 + 资源
不管是通过什么样的方式,其核心原理就是,监控应用流量的qps或者并发线程数的指标,然后去判断这些指标的阈值去对流量进行控制,防止瞬时流量高峰导致系统被压垮。
SphU.entry(resourceName) ->
正常,则通过
被限制,抛出 FlowException( FlowException extends BlockException)
同一个资源也可以创建不同的规则,这个主要是通过List rules=new ArrayList<>();实现的,如果有多个规则,会去遍历,如果发现那个规则被触发则就执行,否则就顺利通过。
一个规则由什么组成?
- resource 资源
- count 阈值
- grade 类型(基于QPS 还是 并发线程数)
- limitApp,针对的调用来源.
- strategy , 调用关系限流
- controlBehavior . (直接拒绝, 冷启动,匀速排队)