本文主要是介绍限流、熔断和降级(持续更新中~),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
1、限流
1.1 什么是限流
限流(Rate Limiting)是一种常用的技术手段,用于控制系统对资源的访问速率,确保系统的稳定性和可靠性。在分布式系统、Web服务、API接口等场景中,限流尤为重要。通过限制请求的频率或数量,可以有效防止因突发流量导致系统过载、崩溃或资源耗尽等问题。限流不仅保护了服务提供者的利益,也提升了用户体验。
1.2 限流的常见场景
- API接口保护:防止恶意用户或爬虫程序对API进行高频访问,消耗大量服务器资源。
- 分布式系统:在微服务架构中,限制各服务间的调用频率,避免服务间的级联失败。
- 数据库访问:限制对数据库的查询或写入操作,防止数据库因压力过大而响应缓慢或崩溃。
- 网络带宽控制:在网络设备或服务器上实施带宽限制,确保关键业务的网络传输质量。
1.3 限流策略
- 基于IP的限流:对每个访问者的IP地址进行限流,防止单个IP的恶意访问。
- 基于用户的限流:对注册用户进行限流,根据用户身份或角色分配不同的请求额度。
- 基于接口的限流:对不同的API接口分别设置限流规则,根据接口的重要性和访问频率进行调整。
- 基于时间段的限流:在特定的时间段内对请求进行限流,如高峰期限制请求频率,低峰期放宽限制。
1.4 限流算法
-
固定窗口算法:将时间划分为固定的窗口,每个窗口内统计请求数量,超过阈值则拒绝服务。该算法实现简单,但可能存在临界问题,即窗口切换时突然允许大量请求通过。
-
滑动窗口算法:相对于固定窗口,滑动窗口算法允许窗口在时间轴上滑动,通过两个固定窗口(当前窗口和上一个窗口)的叠加来统计请求数量,解决了临界问题,但实现复杂度稍高。
-
漏桶算法(Leaky Bucket):将请求视为水滴,以固定速率从桶中漏出。如果水滴到达速率超过漏出速率,则桶满后多余的水滴将被丢弃。该算法平滑了请求的突发流量,但可能导致低优先级请求长时间等待。
-
令牌桶算法(Token Bucket):与漏桶算法类似,但令牌桶以固定速率向桶中添加令牌(代表服务容量)。当请求到达时,如果桶中有足够的令牌,则请求被处理并消耗一个令牌;否则,请求被限流。该算法既能应对突发流量,又能保证请求的公平性。
2、熔断
2.1 什么是熔断
熔断(Circuit Breaker)模式是一种用于处理分布式系统中因服务调用失败而可能导致系统雪崩效应的保护机制。它借用了电路中的“熔断器”概念,当电流过大时,熔断器会自动切断电路,以保护整个电路系统不被烧毁。在分布式系统中,熔断器用于监控服务调用的健康状况,并在检测到异常(如服务调用失败率过高、响应时间过长等)时,自动切断对该服务的调用,从而防止故障在系统中蔓延,保障系统的整体稳定性和可用性。
2.2 熔断的目的
- 防止系统雪崩:当某个服务出现故障时,如果没有有效的隔离措施,故障可能会迅速扩散到整个系统,导致系统雪崩。熔断机制能够在服务故障时及时切断调用链,防止故障扩散。
- 提升系统弹性:通过熔断机制,系统能够在面对服务故障时保持一定的弹性,即能够在故障恢复后快速恢复正常服务,减少因故障导致的服务中断时间。
- 优化用户体验:在熔断期间,系统可以返回预设的降级响应(如默认值、缓存数据或错误信息),减少用户等待时间和错误率,提升用户体验。
2.3 熔断的常见场景
-
服务调用超时:当下游服务的响应时间超过预设的阈值时,如果继续调用可能会导致上游服务资源耗尽,影响整体系统性能。此时,熔断机制可以切断对下游服务的调用,避免超时问题进一步恶化。
-
服务调用失败率过高:如果某个服务的调用失败率达到一定阈值,说明该服务可能存在问题,继续调用可能会浪费系统资源并影响用户体验。熔断机制可以在此时介入,切断对该服务的调用,并返回降级响应。
-
系统资源紧张:在高峰期或系统资源不足时,为了保障核心功能的稳定运行,可以通过熔断机制对部分非核心服务进行降级处理,释放系统资源给关键服务使用。
-
硬件故障或网络问题:当下游服务所在的服务器或网络出现故障时,可能导致服务不可用。熔断机制可以及时发现并切断对故障服务的调用,防止故障扩散。
-
缓存击穿或穿透:在缓存服务中,如果大量请求同时访问未命中缓存的数据(缓存击穿)或不存在的数据(缓存穿透),可能会直接对数据库造成压力。此时,可以通过熔断机制对这类请求进行限流或降级处理。
2.4 熔断策略
-
基于失败率的熔断策略:当服务调用的失败率达到预设的阈值时,触发熔断。这种策略适用于对服务稳定性要求较高的场景,可以确保在服务出现问题时及时切断调用链。
-
基于响应时间的熔断策略:当服务调用的响应时间超过预设的阈值时,触发熔断。这种策略适用于对服务响应时间有严格要求的场景,如在线交易系统、实时数据处理等。
-
基于异常比例的熔断策略:当单位统计时长内异常请求(如抛出异常、返回错误码等)的比例超过设定的阈值时,触发熔断。这种策略适用于服务出现不稳定情况、异常情况较多的场景。
-
基于异常数的熔断策略:当单位统计时长内异常请求的数量超过设定的阈值时,触发熔断。这种策略适用于对异常请求数量敏感的场景,如高并发的Web服务中突然出现大量异常请求的情况。
-
滑动窗口熔断策略:通过滑动窗口来记录一段时间内的请求情况,并根据窗口内的请求失败率或响应时间等指标来判断是否触发熔断。这种策略可以更加灵活地应对系统负载的变化。
-
半开状态策略:在熔断器处于开启状态一段时间后,进入半开状态,允许部分请求通过以测试服务是否已恢复正常。如果测试请求成功,则熔断器关闭;如果测试请求失败,则熔断器重新进入开启状态并延长休眠时间。这种策略有助于在保障系统稳定性的同时,尽快恢复对服务的调用。
2.5 熔断工作流程
- 闭合状态:熔断器处于正常工作状态,允许服务调用通过。此时,系统会监控服务调用的健康状况,如失败率、响应时间等。
- 开启状态:当服务调用满足熔断条件(如失败率达到阈值)时,熔断器进入开启状态,自动切断对该服务的调用。此时,所有对该服务的调用都将直接返回降级响应,不再实际执行。
- 半开启状态:熔断器在开启一段时间后(称为“休眠时间”),会进入半开启状态。在此状态下,系统会允许少量的服务调用通过,以测试服务是否已恢复正常。如果测试调用成功,熔断器将重新进入闭合状态;如果测试调用失败,熔断器将再次进入开启状态,并延长休眠时间。
3、降级
3.1 什么是降级
降级(Degrade)是分布式系统和高可用架构设计中的一个重要概念,旨在系统资源紧张或发生故障时,通过牺牲部分非核心业务功能或降低服务性能的方式,来保障系统整体的可用性和稳定性。降级是一种自我保护机制,它允许系统在面对压力时,主动减少负载,从而避免系统全面崩溃或资源耗尽。
3.2 降级的常见场景
-
服务依赖故障:当系统依赖的外部服务(如数据库、缓存、第三方API等)出现故障或响应缓慢时,可以通过降级策略来减少对这些服务的依赖,使用本地缓存、备用数据源或模拟数据等方式来保障核心功能的运行。
-
系统资源不足:在高峰期或系统资源紧张时,通过降级非核心功能来释放资源,确保核心功能的稳定运行。例如,在电商大促期间,可以降级非交易相关的功能(如商品评论、推荐系统等),以保障交易系统的流畅运行。
-
服务版本不兼容:在微服务架构中,不同服务之间可能存在版本不兼容的问题。当某个服务升级后,与其他服务存在兼容性问题时,可以通过降级策略来回退到旧版本,以保障系统的整体稳定性。
-
安全或合规性要求:在某些情况下,为了满足安全或合规性要求,可能需要降级部分功能或数据。例如,在发现数据泄露风险时,可以降级涉及敏感数据的服务,以防止数据进一步泄露。
-
功能优先级调整:在业务需求发生变化时,可能需要重新评估各功能的优先级。通过降级非优先功能,可以确保有限的资源被用于支持最重要的业务场景。
-
性能测试和故障演练:在进行性能测试或故障演练时,为了模拟真实环境下的系统压力,可以主动对部分服务进行降级处理,以观察系统的表现和恢复能力。
3.3 降级策略
- 开关降级:通过配置开关来启用或禁用某些功能或服务,实现快速降级。
- 限流降级:当系统流量超过预设阈值时,对部分请求进行限流或拒绝服务,以减少系统负载。
- 熔断降级:当服务调用失败率达到一定阈值时,自动触发熔断机制,将后续请求直接返回降级响应,避免对下游服务造成更大压力。
- 资源隔离降级:通过隔离不同服务的资源(如线程池、数据库连接等),防止单个服务的故障影响整个系统。当某个服务资源耗尽时,对其进行降级处理,以保障其他服务的正常运行。
- 超时降级:当服务调用超过预设的超时时间时,自动返回降级响应,防止长时间等待导致的资源浪费和用户体验下降。
3.4 降级和熔断的区别
降级和熔断都是系统保护机制,但侧重点不同。熔断侧重于在故障发生时快速切断服务调用链,防止故障扩散;而降级则是在系统资源紧张或故障发生时,通过牺牲部分非核心功能来保障系统整体可用性和稳定性。两者往往结合使用,形成更完善的系统保护体系。
4、 Sentinel 实现方案
Sentinel 提供了 @SentinelResource
注解用于定义资源,并提供了 AspectJ 的扩展用于自动定义资源、处理 BlockException
等。使用 Sentinel Annotation AspectJ Extension 的时候需要引入以下依赖:
<dependency><groupId>com.alibaba.csp</groupId><artifactId>sentinel-core</artifactId><version>1.8.8</version></dependency><dependency><groupId>com.alibaba.csp</groupId><artifactId>sentinel-annotation-aspectj</artifactId><version>1.8.8</version></dependency>
4.1 官方示例
public class TestService {// 对应的 `handleException` 函数需要位于 `ExceptionUtil` 类中,并且必须为 static 函数.@SentinelResource(value = "test", blockHandler = "handleException", blockHandlerClass = {ExceptionUtil.class})public void test() {System.out.println("Test");}// 原函数@SentinelResource(value = "hello", blockHandler = "exceptionHandler", fallback = "helloFallback")public String hello(long s) {return String.format("Hello at %d", s);}// Fallback 函数,函数签名与原函数一致或加一个 Throwable 类型的参数.public String helloFallback(long s) {return String.format("Halooooo %d", s);}// Block 异常处理函数,参数最后多一个 BlockException,其余与原函数一致.public String exceptionHandler(long s, BlockException ex) {// Do some log here.ex.printStackTrace();return "Oops, error occurred at " + s;}
}
需要通过配置的方式将 SentinelResourceAspect
注册为一个 Spring Bean:
@Configuration
public class SentinelAspectConfiguration {@Beanpublic SentinelResourceAspect sentinelResourceAspect() {return new SentinelResourceAspect();}
}
官方文档:https://sentinelguard.io/zh-cn/docs/introduction.html
官方案例:Sentinel/sentinel-demo at master · alibaba/Sentinel · GitHub
4.2 流量控制(自测)
一条限流规则主要由下面几个因素组成,我们可以组合这些元素来实现不同的限流效果:
resource
:资源名,即限流规则的作用对象count
: 限流阈值grade
: 限流阈值类型,QPS 或线程数strategy
: 根据调用关系选择策略
基于QPS/并发数的流量控制
FlowRule.grade 字段控制了统计QPS还是统计并发数。
1、定义流量限制规则
@Component
public class SentinelRuleInitializer {@PostConstructprivate static void initFlowQpsRule() {List<FlowRule> rules = new ArrayList<FlowRule>();FlowRule rule1 = new FlowRule();//定义资源名称rule1.setResource("flowControl");//限流阈值rule1.setCount(1);//限流阈值类型 0-并发数 1-QPSrule1.setGrade(RuleConstant.FLOW_GRADE_QPS);//超出阈值后处理手段 0-直接拒绝 1-冷启动 2-匀速器 3-rule1.setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_DEFAULT);//应用来源限制rule1.setLimitApp(RuleConstant.LIMIT_APP_DEFAULT);rules.add(rule1);FlowRuleManager.loadRules(rules);}
}
2、注入bean
@Configuration
public class SentinelAspectConfiguration {@Beanpublic SentinelResourceAspect sentinelResourceAspect() {return new SentinelResourceAspect();}
}
3、实现接口
注意!!!一开始没注意到踩了坑~
// Fallback 函数,函数签名与原函数一致或加一个 Throwable 类型的参数.
// Block 异常处理函数,参数最后多一个 BlockException,其余与原函数一致.
"函数签名与原函数一致" 这句话在编程中通常指的是在创建或定义一个函数时,该函数的名称、参数列表(包括参数的个数、顺序、类型)以及返回类型(在某些静态类型语言中)与原函数完全相同。(后续测试熔断方案的时候再试一下)
@Slf4j
@RestController
@RequestMapping("/sentinel")
public class SentinelController {private Integer count = 0;@GetMapping("/flow/control")@SentinelResource(value = "flowControl", blockHandler = "handleBlock", fallback = "handleFallback")public String flowControl() {return "hello flow control!" + count++;}// 定义降级逻辑(当触发熔断时调用)public String flowControlFallback() {log.info("发生熔断{}", count);return "发生熔断";}// 定义BlockHandler逻辑(可选,用于在达到限流条件时处理)public String handleBlock(BlockException ex) {log.info("发生降级{}", count);return "发生降级" + ex.getClass().getSimpleName();}
}
4、测试
限流规则是 QPS为1,即1秒最多请求1次,请求正常结果如下:
当快速点击后,请求结果如下:
4.x Sentinel 控制台
官方 jar包下载:https://github.com/alibaba/Sentinel/releases
下载后使用如下命令启动,注意更改最后的文件名sentinel-dashboard.jar
java -Dserver.port=8080 -Dcsp.sentinel.dashboard.server=localhost:8080 -Dproject.name=sentinel-dashboard -jar sentinel-dashboard.jar
应用绑定sentinel dashboard
spring.cloud.sentinel.transport.dashboard = localhost:8081
打开本地页面(我这里更改端口为8081,默认8080):http://localhost:8081/#/login
默认用户名和密码都是 sentinel
进入后即可配置限流、熔断规则等。
这篇关于限流、熔断和降级(持续更新中~)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!