Sentinel 中实现自定义异常返回的核心机制

在 Sentinel 中实现自定义异常返回的核心机制是:通过定制异常处理器,当触发限流、熔断等规则时,系统会返回符合业务需求的响应格式,而非默认的提示信息。

核心概念

Sentinel 的异常返回定制主要依赖两个核心接口:

  1. BlockExceptionHandler:处理 Sentinel 定义的限流 / 熔断等阻塞异常(BlockException),这是自定义返回的核心接口。
  2. UrlBlockHandler(已过时):旧版本接口,新版本推荐使用BlockExceptionHandler。

Sentinel 默认的异常返回是一段简单的文本(如Blocked by Sentinel (flow limiting)),通过实现BlockExceptionHandler,可以自定义返回 JSON 格式、指定 HTTP 状态码等。

Feign 整合 Sentinel
  1. 在pom.xml文件中引入 Sentinel 依赖;
<!--sentinel客户端-->
  <dependency>
  <groupId>com.alibaba.cloud</groupId>
  <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
  </dependency>

如果已经引入Sentinel, 可忽略.
2. 在配置文件中开启Feign对Sentinel的支持

feign:
sentinel:
enabled: true
自定义异常处理器
  1. 创建配置类,实现BlockExceptionHandler接口:
package com.goblin.feign.fallback;
import com.goblin.entity.Product;
import com.goblin.feign.ProductClient;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
/**
* 这是一个容错类, 直接实现了微服务接口。在使用的时候,需要在 FeignClient接口中,使用
* fallback 进行引用
*/
@Component
@Slf4j
public class productClientFallback implements ProductClient {
/**
*
* @param pid
* @return
*/
@Override
public Product findById(Long pid) {
Product product = new Product();
product.setPid(-100L);
product.setPname("商品微服务调用出现异常了,已经进入到了容错方法中");
return product;
}
}
  1. 在Feign调用客户端配置异常类
package com.goblin.feign;
import com.goblin.entity.Product;
import com.goblin.feign.fallback.ProductClientFallbackFactory;
import com.goblin.feign.fallback.productClientFallback;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
/**
* 调用产品微服务
*/
@FeignClient(name = "service-product"/*, path = "/product"*/
,fallback = productClientFallback.class //这种形式不会获取到具体的错误信息
)
public interface ProductClient {
@GetMapping("/product/{pid}")
Product findById(@PathVariable Long pid);
}

这种方式不能获取到异常信息, 还有另一个方式可以获取到异常信息;

自定义异常处理器,方式2
  1. 创建配置类,实现FallbackFactory接口:
package com.goblin.feign.fallback;
import com.goblin.entity.Product;
import com.goblin.feign.ProductClient;
import feign.hystrix.FallbackFactory;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
/**
* 这是一个容错类, 需要实现 FallbackFactory<要为哪个接口产生容错类>,可以拿到具体的错误信息
  * 在使用的时候,需要在 FeignClient接口中使用fallbackFactory进行引用
  */
  @Slf4j
  @Component
  public class ProductClientFallbackFactory implements FallbackFactory<ProductClient> {
    @Override
    public ProductClient create(Throwable throwable) {
    return new ProductClient() {
    @Override
    public Product findById(Long pid) {
    throwable.printStackTrace();
    Product product = new Product();
    product.setPid(-1L);
    return product;
    }
    };
    }
    }
  1. 在Feign调用客户端配置异常类
package com.goblin.feign;
import com.goblin.entity.Product;
import com.goblin.feign.fallback.ProductClientFallbackFactory;
import com.goblin.feign.fallback.productClientFallback;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
/**
* 调用产品微服务
*/
@FeignClient(name = "service-product"/*, path = "/product"*/
/*
fallback 和 fallbackFactory 两个都是引用容错类的, 但是有区别,
1> fallbackFactory 可以拿到容错类中的错误信息
2> 以上两个只能使用一个, 不能同时使用
*/
//,fallback = productClientFallback.class //这种形式不会获取到具体的错误信息
,fallbackFactory = ProductClientFallbackFactory.class // 可以在容错类中获取到具体的错误信息
)
public interface ProductClient {
@GetMapping("/product/{pid}")
Product findById(@PathVariable Long pid);
}
sentinel持久化

现在可以通过Dashboard来为每个Sentinel客户端设置各种各样的规则,但是这里有一个问题,就是这些规则默认是存放在内存中,极不稳定,所以需要将其持久化。
本地文件数据源会定时轮询文件的变更,读取规则。这样我们既可以在应用本地直接修改文件来更
新规则,也可以通过 Sentinel 控制台推送规则。

  1. 创建持久化的类,实现InitFunc接口
package com.goblin.config;
import com.alibaba.csp.sentinel.command.handler.ModifyParamFlowRulesCommandHandler;
import com.alibaba.csp.sentinel.datasource.*;
import com.alibaba.csp.sentinel.init.InitFunc;
import com.alibaba.csp.sentinel.slots.block.authority.AuthorityRule;
import com.alibaba.csp.sentinel.slots.block.authority.AuthorityRuleManager;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRule;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRuleManager;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRuleManager;
import com.alibaba.csp.sentinel.slots.system.SystemRule;
import com.alibaba.csp.sentinel.slots.system.SystemRuleManager;
import com.alibaba.csp.sentinel.transport.util.WritableDataSourceRegistry;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import org.springframework.beans.factory.annotation.Value;
import java.io.File;
import java.io.IOException;
import java.util.List;
//规则持久化
public class FilePersistence implements InitFunc {
@Value("spring.application:name")
private String appcationName;
@Override
public void init() throws Exception {
String ruleDir = System.getProperty("user.home") + "/sentinel-rules//"+appcationName;
String flowRulePath = ruleDir + "/flow-rule.json";
String degradeRulePath = ruleDir + "/degrade-rule.json";
String systemRulePath = ruleDir + "/system-rule.json";
String authorityRulePath = ruleDir + "/authority-rule.json";
String paramFlowRulePath = ruleDir + "/param-flow-rule.json";
this.mkdirIfNotExits(ruleDir);
this.createFileIfNotExits(flowRulePath);
this.createFileIfNotExits(degradeRulePath);
this.createFileIfNotExits(systemRulePath);
this.createFileIfNotExits(authorityRulePath);
this.createFileIfNotExits(paramFlowRulePath);
// 流控规则
ReadableDataSource<String, List<FlowRule>> flowRuleRDS = new
  FileRefreshableDataSource<>(
    flowRulePath,
    flowRuleListParser
    );
    FlowRuleManager.register2Property(flowRuleRDS.getProperty());
    WritableDataSource<List<FlowRule>> flowRuleWDS = new
      FileWritableDataSource<>(
        flowRulePath,
        this::encodeJson
        );
        WritableDataSourceRegistry.registerFlowDataSource(flowRuleWDS);
        // 降级规则
        ReadableDataSource<String, List<DegradeRule>> degradeRuleRDS = new
          FileRefreshableDataSource<>(
            degradeRulePath,
            degradeRuleListParser
            );
            DegradeRuleManager.register2Property(degradeRuleRDS.getProperty());
            WritableDataSource<List<DegradeRule>> degradeRuleWDS = new
              FileWritableDataSource<>(
                degradeRulePath,
                this::encodeJson
                );
                WritableDataSourceRegistry.registerDegradeDataSource(degradeRuleWDS);
                // 系统规则
                ReadableDataSource<String, List<SystemRule>> systemRuleRDS = new
                  FileRefreshableDataSource<>(
                    systemRulePath,
                    systemRuleListParser
                    );
                    SystemRuleManager.register2Property(systemRuleRDS.getProperty());
                    WritableDataSource<List<SystemRule>> systemRuleWDS = new
                      FileWritableDataSource<>(
                        systemRulePath,
                        this::encodeJson);
                        WritableDataSourceRegistry.registerSystemDataSource(systemRuleWDS);
                        // 授权规则
                        ReadableDataSource<String, List<AuthorityRule>> authorityRuleRDS = new
                          FileRefreshableDataSource<>(
                            authorityRulePath,
                            authorityRuleListParser
                            );
                            AuthorityRuleManager.register2Property(authorityRuleRDS.getProperty());
                            WritableDataSource<List<AuthorityRule>> authorityRuleWDS = new
                              FileWritableDataSource<>(
                                authorityRulePath,
                                this::encodeJson
                                );
                                WritableDataSourceRegistry.registerAuthorityDataSource(authorityRuleWDS);
                                // 热点参数规则
                                ReadableDataSource<String, List<ParamFlowRule>> paramFlowRuleRDS = new
                                  FileRefreshableDataSource<>(
                                    paramFlowRulePath,
                                    paramFlowRuleListParser
                                    );
                                    ParamFlowRuleManager.register2Property(paramFlowRuleRDS.getProperty());
                                    WritableDataSource<List<ParamFlowRule>> paramFlowRuleWDS = new
                                      FileWritableDataSource<>(
                                        paramFlowRulePath,
                                        this::encodeJson
                                        );
                                        ModifyParamFlowRulesCommandHandler.setWritableDataSource(paramFlowRuleWDS);
                                        }
                                        private Converter<String, List<FlowRule>> flowRuleListParser = source ->
                                          JSON.parseObject(
                                          source,
                                          new TypeReference<List<FlowRule>>() {
                                            }
                                            );
                                            private Converter<String, List<DegradeRule>> degradeRuleListParser = source
                                              -> JSON.parseObject(
                                              source,
                                              new TypeReference<List<DegradeRule>>() {
                                                }
                                                );
                                                private Converter<String, List<SystemRule>> systemRuleListParser = source ->
                                                  JSON.parseObject(
                                                  source,new TypeReference<List<SystemRule>>() {
                                                    }
                                                    );
                                                    private Converter<String, List<AuthorityRule>> authorityRuleListParser =
                                                      source -> JSON.parseObject(
                                                      source,
                                                      new TypeReference<List<AuthorityRule>>() {
                                                        }
                                                        );
                                                        private Converter<String, List<ParamFlowRule>> paramFlowRuleListParser =
                                                          source -> JSON.parseObject(
                                                          source,
                                                          new TypeReference<List<ParamFlowRule>>() {
                                                            }
                                                            );
                                                            private void mkdirIfNotExits(String filePath) throws IOException {
                                                            File file = new File(filePath);
                                                            if (!file.exists()) {
                                                            file.mkdirs();
                                                            }
                                                            }
                                                            private void createFileIfNotExits(String filePath) throws IOException {
                                                            File file = new File(filePath);
                                                            if (!file.exists()) {
                                                            file.createNewFile();
                                                            }
                                                            }
                                                            private <T> String encodeJson(T t) {
                                                              return JSON.toJSONString(t);
                                                              }
                                                              }

2.在资源目录添加目并配置持久化类
持久化

持久化验证

shop-order 微服务重启服务, 在Sentinel控制台配置流控数据。重启微服务,在Sentinel控制台查看流控规则,限流规则依然存在,说明持久化成功了