☰
Spring Cloud Alibaba Sentinel @SentinelResource
2026/9/25 16:50:13 网站建设 项目流程

一、前言

Sentinel提供了@SentinelResource注解用于定义资源,并提供可选的异常回退和Block回退。异常回退指的是@SentinelResource注解标注的方法发生Java异常时的回退处理;Block回退指的是当@SentinelResource资源访问不符合Sentinel控制台定义的规则时的回退(默认返回Blocked by Sentinel (flow limiting))。这节简单记录下该注解的用法。

二、框架搭建

2.1 项目搭建

使用IDEA创建一个maven项目,artifactId为spring-cloud-alibaba-sentinelresource,然后在其下面创建两个Module(Spring Boot项目),artifactId分别为consumer和provider,充当服务消费端和服务提供端:

spring-cloud-alibaba-sentinelresource的pom内容:

<?xml version="1.0" encoding="UTF-8"?><projectxmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.3.2.RELEASE</version><relativePath/><!-- lookup parent from repository --></parent><groupId>com.wno704</groupId><artifactId>spring-cloud-alibaba-sentinelresource</artifactId><version>0.0.1-SNAPSHOT</version><name>Alibaba-SentinelResource</name><description>Demo project for Spring Boot</description><packaging>pom</packaging><properties><java.version>1.8</java.version><spring-cloud-alibaba.version>2.2.3.RELEASE</spring-cloud-alibaba.version></properties><modules><module>consumer</module><module>provider</module></modules><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-devtools</artifactId><scope>runtime</scope><optional>true</optional></dependency><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope><exclusions><exclusion><groupId>org.junit.vintage</groupId><artifactId>junit-vintage-engine</artifactId></exclusion></exclusions></dependency></dependencies><dependencyManagement><dependencies><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-alibaba-dependencies</artifactId><version>${spring-cloud-alibaba.version}</version><type>pom</type><scope>import</scope></dependency></dependencies></dependencyManagement><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build></project>

引入了spring-boot-starter-web和spring-cloud-starter-alibaba-nacos-discovery服务注册发现依赖。

provider的pom的内容如下所示:

<?xml version="1.0" encoding="UTF-8"?><projectxmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>com.wno704</groupId><artifactId>spring-cloud-alibaba-sentinelresource</artifactId><version>0.0.1-SNAPSHOT</version><relativePath>../pom.xml</relativePath></parent><artifactId>provider</artifactId><name>Alibaba-Provider</name><description>Demo project for Spring Boot</description><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build></project>

consumer的pom内容如下:

<?xml version="1.0" encoding="UTF-8"?><projectxmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>com.wno704</groupId><artifactId>spring-cloud-alibaba-sentinelresource</artifactId><version>0.0.1-SNAPSHOT</version><relativePath>../pom.xml</relativePath></parent><artifactId>consumer</artifactId><name>Alibaba-Consumer</name><description>Demo project for Spring Boot</description><dependencies><dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-sentinel</artifactId></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build></project>

因为要演示在消费端使用@SentinelResource注解,所以我们引入了spring-cloud-starter-alibaba-sentinel依赖。

2.2 项目配置

provider的配置文件application.yml内容如下:

server:port:8900spring:application:name:providercloud:nacos:server-addr:127.0.0.1:8848

配置了端口号,服务名和nacos地址。

consumer的配置文件application.yml内容如下:

server:port:8901spring:application:name:consumercloud:nacos:server-addr:127.0.0.1:8848sentinel:transport:dashboard:127.0.0.1:8080port:8719

配置了端口号,服务名,nacos地址和sentinel控制台地址等。

三、基本用法

3.1 创建controller

我们在provider下添加一个REST资源。在provider的cc.mrbird.provider目录下新建controller包,然后在该包下新建GoodsController:

@RestController@RequestMapping("goods")publicclassGoodsController{@GetMapping("buy/{name}/{count}")publicStringbuy(@PathVariableStringname,@PathVariableIntegercount){returnString.format("购买%d份%s",count,name);}}

接着在consumer端通过Ribbon消费这个资源。在consumer的com.wno704.alibaba的目录下新建config文件夹,新建ConsumerConfig配置类,注册RestTemplate。

@ConfigurationpublicclassConsumerConfig{@BeanpublicRestTemplaterestTemplate(){returnnewRestTemplate();}@BeanpublicSentinelResourceAspectsentinelResourceAspect(){returnnewSentinelResourceAspect();}}

在consumer的com.wno704.alibaba下新建controller包,然后在该包下新建ConsumeController:

@RestControllerpublicclassConsumeController{@AutowiredprivateLoadBalancerClientloadBalancerClient;@AutowiredprivateRestTemplaterestTemplate;@GetMapping("buy/{name}/{count}")@SentinelResource(value="buy",fallback="buyFallback",blockHandler="buyBlock")publicStringbuy(@PathVariableStringname,@PathVariableIntegercount){if(count>=20){thrownewIllegalArgumentException("购买数量过多");}if("wno704".equalsIgnoreCase(name)){thrownewNullPointerException("已售罄");}Map<String,Object>params=newHashMap<>(2);params.put("name",name);params.put("count",count);ServiceInstanceserviceInstance=loadBalancerClient.choose("provider");Stringpath=String.format("http://%s:%s/goods/buy/{name}/{count}",serviceInstance.getHost(),serviceInstance.getPort());returnthis.restTemplate.getForEntity(path,String.class,params).getBody();}// 异常回退publicStringbuyFallback(@PathVariableStringname,@PathVariableIntegercount,Throwablethrowable){returnString.format("【进入fallback方法】购买%d份%s失败,%s",count,name,throwable.getMessage());}// sentinel回退publicStringbuyBlock(@PathVariableStringname,@PathVariableIntegercount,BlockExceptione){returnString.format("【进入blockHandler方法】购买%d份%s失败,当前购买人数过多,请稍后再试",count,name);}}

在buy方法中,我们通过Ribbon的RestTemplate访问provider的/goods/buy接口。当count参数大于20或者name参数的值为miband的时候,方法将抛出异常。buy方法上使用@SentinelResource注解标注,标识为一个sentinel资源,资源名称为buy,并且配置了fallback方法和blockHandler方法。

如前面所说,当buy方法本身抛出异常时,会进入fallback指定的回退方法中;当buy方法调用不符合sentinel控制台规定的规则(如流控规则,降级规则等)时,会进入blockHander指定的block方法中。为了确保成功地进入回退方法(成功反射),它们必须满足以下规则:

  • 函数访问范围需要是public;
  • Fallback函数,函数签名与原函数一致或末尾加一个Throwable类型的参数;
  • Block异常处理函数,参数最后多一个BlockException,其余与原函数一致。

3.2 测试

启动provider、consumer、nacos和sentinel控制台,http访问:http://localhost:8901/buy/ipad/2:

我们在sentinel控制台中添加如下流控规则:

QPS阈值为2。

然后快速访问http://localhost:8901/buy/ipad/2:

可以看到,当方法访问不符合sentinel控制台规则时,进入的是blockHandler指定的回退方法。

如果访问:http://localhost:8901/buy/ipad/21

或者:http://localhost:8901/buy/wno704/2

方法自身抛出异常引发回退,进入的是fallback指定的回退方法。

四、其他属性

在当前类中编写回退方法会使得代码变得冗余耦合度高,我们可以将回退方法抽取出来到一个指定类中。

在com.wno704.alibaba包下新建reveal包,然后在该包下新建BuyBlockHandler:

publicclassBuyBlockHandler{// sentinel回退publicstaticStringbuyBlock(@PathVariableStringname,@PathVariableIntegercount,BlockExceptione){returnString.format("【进入blockHandler方法】购买%d份%s失败,当前购买人数过多,请稍后再试",count,name);}}

可以看到我们只是将buyBlock方法挪到了BuyBlockHandler中,不过这里的方法必须是static的。

接着新建BuyFallBack:

publicclassBuyFallBack{// 异常回退publicstaticStringbuyFallback(@PathVariableStringname,@PathVariableIntegercount,Throwablethrowable){returnString.format("【进入fallback方法】购买%d份%s失败,%s",count,name,throwable.getMessage());}}

这样BuyController的代码就可以精简为:

@RestControllerpublicclassConsumeController{@AutowiredprivateLoadBalancerClientloadBalancerClient;@AutowiredprivateRestTemplaterestTemplate;@GetMapping("buy/{name}/{count}")@SentinelResource(value="buy",fallback="buyFallback",fallbackClass=BuyFallBack.class,blockHandler="buyBlock",blockHandlerClass=BuyBlockHandler.class)publicStringbuy(@PathVariableStringname,@PathVariableIntegercount){if(count>=20){thrownewIllegalArgumentException("购买数量过多");}if("wno704".equalsIgnoreCase(name)){thrownewNullPointerException("已售罄");}Map<String,Object>params=newHashMap<>(2);params.put("name",name);params.put("count",count);ServiceInstanceserviceInstance=loadBalancerClient.choose("provider");Stringpath=String.format("http://%s:%s/goods/buy/{name}/{count}",serviceInstance.getHost(),serviceInstance.getPort());returnthis.restTemplate.getForEntity(path,String.class,params).getBody();}}

此外我们也可以当遇到某个类型的异常时,不进行回退。比如:

@RestControllerpublicclassConsumeController{@AutowiredprivateLoadBalancerClientloadBalancerClient;@AutowiredprivateRestTemplaterestTemplate;@GetMapping("buy/{name}/{count}")@SentinelResource(value="buy",fallback="buyFallback",fallbackClass=BuyFallBack.class,blockHandler="buyBlock",blockHandlerClass=BuyBlockHandler.class,exceptionsToIgnore=NullPointerException.class)publicStringbuy3(@PathVariableStringname,@PathVariableIntegercount){if(count>=20){thrownewIllegalArgumentException("购买数量过多");}if("wno704".equalsIgnoreCase(name)){thrownewNullPointerException("已售罄");}Map<String,Object>params=newHashMap<>(2);params.put("name",name);params.put("count",count);ServiceInstanceserviceInstance=loadBalancerClient.choose("provider");Stringpath=String.format("http://%s:%s/goods/buy/{name}/{count}",serviceInstance.getHost(),serviceInstance.getPort());returnthis.restTemplate.getForEntity(path,String.class,params).getBody();}}

exceptionsToIgnore指定,当遇到空指针异常时,不回退。

重启consumer,http访问:http://localhost:8901/buy/wno704/2:

可以看到,此次并没有进行回退,而是直接返回error page。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询