文章目录
-
-
-
-
-
- [Sentinel 中实现自定义异常返回的核心机制](#Sentinel 中实现自定义异常返回的核心机制)
- 核心概念
- [Feign 整合 Sentinel](#Feign 整合 Sentinel)
- 自定义异常处理器
- 自定义异常处理器,方式2
- sentinel持久化
- 持久化验证
-
-
-
-
Sentinel 中实现自定义异常返回的核心机制
在 Sentinel 中实现自定义异常返回的核心机制是:通过定制异常处理器,当触发限流、熔断等规则时,系统会返回符合业务需求的响应格式,而非默认的提示信息。
核心概念
Sentinel 的异常返回定制主要依赖两个核心接口:
- BlockExceptionHandler:处理 Sentinel 定义的限流 / 熔断等阻塞异常(BlockException),这是自定义返回的核心接口。
- UrlBlockHandler(已过时):旧版本接口,新版本推荐使用BlockExceptionHandler。
Sentinel 默认的异常返回是一段简单的文本(如Blocked by Sentinel (flow limiting)),通过实现BlockExceptionHandler,可以自定义返回 JSON 格式、指定 HTTP 状态码等。
Feign 整合 Sentinel
- 在pom.xml文件中引入 Sentinel 依赖;
xml
<!--sentinel客户端-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
如果已经引入Sentinel, 可忽略.
- 在配置文件中开启Feign对Sentinel的支持
yaml
feign:
sentinel:
enabled: true
自定义异常处理器
- 创建配置类,实现BlockExceptionHandler接口:
java
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;
}
}
- 在Feign调用客户端配置异常类
java
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
- 创建配置类,实现FallbackFactory接口:
java
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;
}
};
}
}
- 在Feign调用客户端配置异常类
java
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 控制台推送规则。
- 创建持久化的类,实现InitFunc接口
java
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控制台查看流控规则,限流规则依然存在,说明持久化成功了