目录
使用Sentinel实现限流降级等效果通常需要先把需要保护的资源定义好,之后再基于定义好的资源为其配置限流降级等规则。
Sentinel对于主流框架,例如 Web Servlet、Dubbo、Spring Cloud、gRPC、Spring WebFlux、Reactor 等都做了适配,具体可以参考主流框架适配。只需要引入对应的依赖即可方便地整合 Sentinel。
但框架适配主要是对一些http或RPC接口做资源定义,如果涉及到一些代码块或者方法是无法做资源定义的,这种情况下就需要通过编码方式自定义资源,并通过捕获异常的方式实现限流或降级的逻辑。
使用SphU的API实现自定义资源
SphU类在sentinel-core依赖中,主要用于对资源的访问控制。当系统想要执行某个资源操作时,通过调用 SphU.entry 方法来检查该资源是否受到流量控制。
- 如果资源当前处于限流状态,那么 SphU.entry 会抛出 BlockException 异常,表示请求被限流,此时系统可以进行降级操作。
- 如果资源没有受到限流,那么 SphU.entry 会返回一个 Entry 对象,表示一次资源操作开始,系统可以继续执行相关的业务代码。
- 注意SphU.entry 需要和 entry.exit 一起配合使用,确保在业务代码执行完后执行 entry.exit
BlockException
BlockException是sentinel流控触发后的异常类,其下包含很多个子类,分别对应不同的场景:
- FlowException 限流异常
- ParamFlowException 热点参数限流的异常
- DegradeException 降级异常
- AuthorityException 授权规则异常
- SystemBlockException 系统规则异常
使用SphU的API前,需要确保项目中直接或间接引入了sentinel-core的依赖(通常框架适配的stater中已经引入了)
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-core</artifactId>
<version>1.8.6</version>
</dependency>
下面是一个通过SphU自定义资源的实例代码:
public PriceDTO queryPrice(String skuCode) {
PriceParam param = new PriceParam();
param.setSkuCode(skuCode);
Entry entry = null;
try {
entry = SphU.entry(SentinelResourceConstant.MARKETING_SPECIAL_ACTIVITY_QUERY_SHOP_SKU_PRICE);
return productFacade.queryPrice(param).unboxing();
}catch(FlowException e) {
log.error("查询商品价格接口触发限流,skuCode:{}", skuCode);
return null;
}catch(DegradeException e) {
log.error("查询商品价格接口触发降级,skuCode:{}", skuCode);
return null;
} finally {
if (entry != null) {
entry.exit();
}
}
}
使用@SentinelResource注解定义资源
上面通过SphU API自定义资源可以最小粒度的控制要保护的资源,但是侵入较大,增加了代码的复杂性。而另外一中方式就是使用@SentinelResource 注解,这种方式对代码入侵程度相对较低。
SentinelResourceAspect
使用@SentinelResource 注解来实现资源定义,原理与上面的API其实是一样的,Sentinel 中定义了SentinelResourceAspect 通过切面方式拦截后同样通过SphU API来实现流控功能。
既然注解方式是通过切面来实现的,那么在使用注解方式进行资源定义的前提就是先通过配置的方式将 SentinelResourceAspect 注册为一个 Spring Bean,有了这个bean以后@SentinelResource注解才可以生效。
@Configuration
public class SentinelAspectConfiguration {
@Bean
public SentinelResourceAspect sentinelResourceAspect() {
return new SentinelResourceAspect();
}
}
开启配置后即可在代码中使用@SentinelResource注解对某个方法进行Sentinel资源定义。
@Service
public class TestService {
// 资源定义并设置流控处理
@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;
}
}