文章目录
前言
上一章我们已经完成了对Sentinel的适配工作,这里来学习它的更多用法。
一、注解埋点支持
Sentinel 提供了 @SentinelResource 注解用于定义资源,并提供了 AspectJ 的扩展用于自动定义资源、处理 BlockException 等。
使用 Sentinel Annotation AspectJ Extension 的时候需要引入以下依赖:
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-annotation-aspectj</artifactId>
<version>x.y.z</version>
</dependency>
那么为什么我们没有引入这个依赖包也能使用呢,是spring-cloud-starter-alibaba-sentinel
启动器帮我们引入进来了,真香呀!
二、@SentinelResource 注解
注意:注解方式埋点不支持 private 方法。
@SentinelResource 用于定义资源,并提供可选的异常处理和 fallback 配置项。 @SentinelResource 注解包含以下属性:
value
:资源名称,必需项(不能为空)entryType
:entry
类型,可选项(默认为EntryType.OUT
)blockHandler
/blockHandlerClass
:blockHandler
对应处理BlockException
的函数名称,可选项。
blockHandler
函数访问范围需要是public
,返回类型需要与原方法相匹配,参数类型需要和原方法相匹配并且最后加一个额外的参数,类型为BlockException
。
blockHandler
函数默认需要和原方法在同一个类中。若希望使用其他类的函数,则可以指定blockHandlerClass
为对应的类的Class
对象,注意对应的函数必需为static
函数,否则无法解析。
fallback
:fallback
函数名称,可选项,用于在抛出异常的时候提供fallback
处理逻辑。
fallback
函数可以针对所有类型的异常(除了exceptionsToIgnore
里面排除掉的异常类型)进行处理。fallback
函数签名和位置要求:返回值类型必须与原函数返回值类型一致;方法参数列表需要和原函数一致,或者可以额外多一个Throwable
类型的参数用于接收对应的异常。
fallback
函数默认需要和原方法在同一个类中。若希望使用其他类的函数,则可以指定fallbackClass
为对应的类的Class
对象,注意对应的函数必需为static
函数,否则无法解析。
defaultFallback
(since 1.6.0):默认的fallback
函数名称,可选项,通常用于通用的fallback
逻辑(即可以用于很多服务或方法)。
默认fallback
函数可以针对所有类型的异常(除了exceptionsToIgnore
里面排除掉的异常类型)进行处理。若同时配置了fallback
和defaultFallback
,则只有fallback
会生效。defaultFallback
函数签名要求:返回值类型必须与原函数返回值类型一致;方法参数列表需要为空,或者可以额外多一个Throwable
类型的参数用于接收对应的异常。
defaultFallback
函数默认需要和原方法在同一个类中。若希望使用其他类的函数,则可以指定fallbackClass
为对应的类的Class
对象,注意对应的函数必需为static
函数,否则无法解析。
exceptionsToIgnore
(since 1.6.0):用于指定哪些异常被排除掉,不会计入异常统计中,也不会进入fallback
逻辑中,而是会原样抛出。
注:1.6.0 之前的版本 fallback 函数只针对降级异常(DegradeException)进行处理,不能针对业务异常进行处理。
特别地,若 blockHandler
和 fallback
都进行了配置,则被限流降级而抛出 BlockException
时只会进入 blockHandler
处理逻辑。若未配置 blockHandler
、fallback
和 defaultFallback
,则被限流降级时会将 BlockException
直接抛出。
相信大家看到这里有点麻了,跟着我的步骤一定能学会的,follow me.
三、实战
这里我们对上章案例进一步调整,查看Sentinel注解的作用。
1. 准备
这里以流控来演示
2. 纯资源定义
@Service
public class SentinelServiceImpl implements SentinelService {
@Override
@SentinelResource(value = "sayHello")
public String sayHello(String name) {
return "Hello, " + name;
}
}
3. 添加资源配置
package org.example.nacos.provider.service.impl;
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.alibaba.csp.sentinel.slots.block.flow.FlowException;
import org.example.nacos.provider.service.SentinelService;
import org.springframework.stereotype.Service;
/**
* Create by zjg on 2024/8/26
*/
@Service
public class SentinelServiceImpl implements SentinelService {
@Override
@SentinelResource(value = "sayHello",blockHandler = "handleException",blockHandlerClass = {SentinelServiceImpl.class})
public String sayHello(String name) {
return "Hello, " + name;
}
public static String handleException(String name, BlockException ex){
if(ex instanceof FlowException){
return "流量激增,服务扛不住了!";
}
return "服务正忙,请稍后再试!";
}
}
四、熔断(fallback)
这里分开讲,是为了避免冲突,这几个参数的策略是,你全都配置上没问题,但铜锣湾只能有一个大哥,那就是山鸡!
1. 业务代码
1.1 Controller
package org.example.nacos.provider.controller;
import org.example.nacos.provider.service.SentinelService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
/**
* Create by zjg on 2024/8/26
*/
@RestController
public class SentinelController {
@Autowired
private SentinelService service;
@GetMapping(value = "/hello/{name}")
public String apiHello(@PathVariable("name") String name) {
return service.sayHello(name);
}
@GetMapping(value = "/bye/{name}")
public String apiBye(@PathVariable("name") String name) {
return service.sayBye(name);
}
}
1.2 Service
package org.example.nacos.provider.service;
/**
* Create by zjg on 2024/8/26
*/
public interface SentinelService {
public String sayHello(String name);
public String sayBye(String name);
}
1.3 ServiceImpl
package org.example.nacos.provider.service.impl;
import com.alibaba.csp.sentinel.annotation.SentinelResource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.alibaba.csp.sentinel.slots.block.flow.FlowException;
import org.example.nacos.provider.service.SentinelService;
import org.springframework.stereotype.Service;
import java.util.Random;
/**
* Create by zjg on 2024/8/26
*/
@Service
public class SentinelServiceImpl implements SentinelService {
@Override
@SentinelResource(value = "sayHello",blockHandler = "handleException")
public String sayHello(String name) {
return "Hello, " + name;
}
public static String handleException(String name, BlockException ex){
if(ex instanceof FlowException){
return "流量激增,服务扛不住了!";
}
return "服务正忙,请稍后再试!";
}
@Override
@SentinelResource(value = "sayBye",fallback = "fallback")
public String sayBye(String name) {
int i = new Random().nextInt(10);
if(i%2==0){
i=1/0;
}
return "Bye, " + name;
}
public String fallback(String name,Throwable throwable){
System.out.println(throwable.getMessage());
return "系统异常,请稍后重试!";
}
}
2分之1的失败几率,非常高呦!
2. 熔断配置
3. 熔断测试
总结
注意 blockHandler 函数会在原方法被限流/降级/系统保护的时候调用,而 fallback 函数会针对所有类型的异常。