首页
学习
活动
专区
工具
TVP
发布
精选内容/技术社群/优惠产品,尽在小程序
立即前往

深度解析微服务高并发实现开关降级:扩展Sentinel实现开关降级

扩展Sentinel实现开关降级

Sentinel实现了插件功能,支持将自定义处理器插槽(ProcessorSlot)通过SPI注册到ProcessorSlotChain中,或者通过自实现SlotChainBuilder构建ProcessorSlotChain,将自定义处理器插槽注册到ProcessorSlotChain中。因此,我们可以通过自定义ProcessorSlot为Sentinel添加开关降级功能。

与限流、熔断降级等流量控制的实现一样,首先定义开关降级规则类,实现loadRulesAPI;然后提供一个Checker,由Checker判断开关是否打开,是否需要拒绝当前请求;最后自定义ProcessorSlot与SlotChainBuilder,实现拦截请求的功能。

与使用AOP实现开关降级有所不同,扩展Sentinel实现开关降级不需要在接口方法或类上添加注解,可以全部通过配置规则实现,这也是为什么选择扩展Sentinel实现开关降级功能的原因。

一个开关通常会控制很多接口,所以一个开关对应一个开关降级规则,一个开关降级规则可配置多个资源。

开关降级规则类SwitchRule的代码如下:

SwitchRule类字段说明如下:

status:开关状态,取值为open或close。

resources:开关控制的资源。

Resources内部类字段说明如下:

include:包含的资源。

exclude:排除的资源。

灵活,不仅只是不需要使用注解,还需要可以灵活地指定开关控制哪些资源,因此,配置开关控制的资源应支持以下几种情况:指定该开关只控制哪些资源(include),或控制除了哪些资源(exclude)的其他资源,或者控制全部资源。

所以,SwitchRule的资源配置与Sentinel的限流、熔断降级规则的资源配置不一样,SwitchRule支持以下3种资源配置方式:

如果不配置resources,则开关作用于全部资源。

如果配置了include,则开关作用于include指定的所有资源。

如果不配置include且配置了exclude,则除exclude指定的资源外,其他资源都受这个开关的控制。

接着实现loadRulesAPI。在Sentinel中,提供loadRulesAPI的类通常被命名为XxxRuleManager,即Xxx规则管理器,所以我们定义开关降级规则管理器的名称为SwitchRuleManager。

SwitchRuleManager的代码如下:

SwitchRuleManager提供了两个API:

loadSwitchRules:用于更新或加载开关降级规则。

getRules:获取当前生效的全部开关降级规则。

同样地,Sentinel一般会将决定规则是否达到触发开关降级的阈值由XxxRuleChecker完成,即Xxx规则检查员。所以我们定义开关降级规则检查员的名称为SwitchRuleChecker,由SwitchRuleChecker检查开关是否打开,若开关打开,则抛出SwitchException,拒绝请求。

SwitchRuleChecker的代码如下:

遍历规则,每个打开状态的开关都有可能控制当前资源,只要其中一个关联到当前资源,就拒绝当前请求。

判断开关状态,如果开关未打开,则跳过。

实现include语意,如果规则配置了include,并且include包含当前资源,则匹配成功,抛出SwitchException以拒绝当前请求。

实现exclude语意,如果规则配置了exclude,并且exclude不包含当前资源,则抛出SwitchException以拒绝当前请求。

整个checkSwitch方法实现的功能:SwitchRuleChecker从SwitchRuleManager中获取配置的开关降级规则,并遍历开关降级规则,如果开关打开,且匹配到当前资源名称被该开关控制,则抛出SwitchException。

SwitchException需继承BlockException,如果抛出的SwitchException没有被捕获,则由全局异常处理器处理。

注意:必须抛出BlockException的子类异常,否则抛出的异常会被资源指标数据统计收集,会影响到熔断降级等功能的准确性。

虽然SwitchRuleChecker使用了for循环遍历开关降级规则,但是一个项目中的开关是很少的,一般只有一个或几个,并且使用hash匹配include与exclude,时间复杂度接近O(1),所以这样实现的开关降级对性能影响并不大。

要使用开关功能,我们还需要自定义处理器插槽:SwitchSlot

SwitchSlot类继承AbstractLinkedProcessorSlot抽象类,负责在entry方法中调用SwitchRuleChecker#checkSwitch方法,检查是否需要拒绝当前请求。

SwitchSlot类的代码如下:

现在,我们需要自定义SlotChainBuilder,即MySlotChainBuilder,将自定义的SwitchSlot添加到ProcessorSlotChain的末尾。当然,可以将SwitchSlot添加到任何位置,因为SwitchSlot没有用到资源指标数据,所以将SwitchSlot放在哪里都不会影响Sentinel的正常工作。

MySlotChainBuilder的代码如下:

如上述代码所示,MySlotChainBuilder继承DefaultSlotChainBuilder只是为了使用DefaultSlotChainBuilder#build方法。若要简化ProcessorSlotChain的构造步骤,则只需要在DefaultSlotChainBuilder构造好的链表尾部添加一个SwitchSlot即可。

但是MySlotChainBuilder还没有生效,只有将MySlotChainBuilder注册到SlotChainBuilder接口的SPI配置文件中后,MySlotChainBuilder才会生效。

具体操作为在当前项目的resources/META-INF/service资源目录下创建一个名称为com.alibaba.csp.sentinel.slotchain.SlotChainBuilder的文件,并在该文件中配置MySlotChainBuilder类的全名,代码如下:

在上述操作完成后,我们在MySlotChainBuilder#build方法中添加断点,然后启动项目,在正常情况下,程序会在该断点停下。但是因为我们并未配置开关降级规则,所以还看不到效果。

为了验证开关降级效果,我们通过硬编码方式添加一个开关配置。

创建SpringBoot配置类,并在配置类的初始化方法中调用SwitchRuleManager#loadRules API添加开关降级规则,代码如下:

上述代码配置了一个开关降级规则,配置了该规则的status字段值为open,该开关降级规则只控制接口(资源)“/v1/test/demo”。

注意:这种配置方式只适用于本地测试,在实际项目中需要通过动态配置实现。

小结

本篇主要介绍如何使用Spring AOP实现开关降级,以及扩展Sentinel实现开关降级。

使用Spring AOP实现开关降级的缺点是配置不灵活:如果让一个开关控制多个接口,则无法实现动态为开关添加或移除其管理的接口;如果让一个开关仅控制一个接口,则需要多个开关配置,不易于管理。

在不使用Sentinel的项目中,使用Spring AOP实现开关降级是一种不错的方式,而如果在项目中已经使用了Sentinel,建议采用扩展Sentinel实现开关降级,并结合动态配置,实现更为灵活的开关降级。

本文给大家讲解的内容是深度解析微服务高并发实现开关降级 :扩展Sentinel实现开关降级

下篇文章给大家讲解的内容是深度解析微服务高并发动态数据源 :实现规则动态配置的两种方式,使用Redis动态数据源

感谢大家的支持!

  • 发表于:
  • 原文链接https://kuaibao.qq.com/s/20230318A00O1000?refer=cp_1026
  • 腾讯「腾讯云开发者社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

相关快讯

扫码

添加站长 进交流群

领取专属 10元无门槛券

私享最新 技术干货

扫码加入开发者社群
领券