配置说明参考:https://blog.csdn.net/m0_46379371/article/details/114904351
使用的依赖包:
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
<version>2021.1</version>
</dependency>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-sentinel-gateway</artifactId>
<version>2021.1</version>
</dependency>
需求:对不同的应用进行不一样的限流策略,并且对特定接口进行通用限流
实现方法:不同应用发送的请求时,由于项目的拦截器在sentinel的拦截器之前,我会拦截所有的请求,在请求头中设置key为LimitRulesKey,value为应用code的键值对。由于项目初始化后,会读取配置配置在redis中的各个应用的限流策略并加载到sentinel中,sentinel会对匹配中拦截规则(例如正则匹配,前缀匹配等)的请求中获取请求的LimitRulesKey键值对,拿redis中的应用的code与LimitRulesKey键值对中的value进行比较,相等就进行此应用code对应的限流策略,不相等就进行配置接口的通用限流。sentinel每30分钟读取一次redis的应用限流策略,更新已配置的限流策略
sentinel的配置类
@Configuration
@Slf4j
public class SentinelConfiguration {
@Resource
private StringRedisTemplate stringRedisTemplate;
@Resource
private CommonAppLimit commonAppLimit;
private final List<ViewResolver> viewResolvers;
private final ServerCodecConfigurer serverCodecConfigurer;
public GatewayConfiguration(ObjectProvider<List<ViewResolver>> viewResolversProvider,
ServerCodecConfigurer serverCodecConfigurer) {
this.viewResolvers = viewResolversProvider.getIfAvailable(Collections::emptyList);
this.serverCodecConfigurer = serverCodecConfigurer;
}
@Bean
@Order(Ordered.HIGHEST_PRECEDENCE)
public JsonSentinelGatewayBlockExceptionHandler jsonSentinelGatewayBlockExceptionHandler() {
return new JsonSentinelGatewayBlockExceptionHandler(viewResolvers, serverCodecConfigurer);
}
@Bean
@Order(-1)
public GlobalFilter sentinelGatewayFilterConfiguration() {
return new SentinelTsfGatewayFilter();
}
@PostConstruct
public void doInit() {
initCustomizedApis();
initGatewayRules();
List<AppNode> appNodes = initPropertyFromRedis();
if (appNodes.size() > 0) {
initCustomizedApisFromRedis(appNodes);
initGatewayRulesFromRedis(appNodes);
}
}
private List<AppNode> initPropertyFromRedis() {
List<AppNode> appNodes = new ArrayList<>();
try {
log.info("=========执行initPropertyFromRedis方法=========");
Set<String> keys = stringRedisTemplate.keys("sentinel*");
if (Objects.isNull(keys)) {
return appNodes;
}
for (String key : keys) {
try {
String sentinelConfig = stringRedisTemplate.opsForValue().get(key);
if (StringUtils.isEmpty(sentinelConfig)) {
continue;
}
log.info("initPropertyFromRedis的sentinelConfig:" + sentinelConfig);
AppNode appNode = JSON.parseObject(sentinelConfig, AppNode.class);
appNodes.add(appNode);
} catch (Exception e) {
log.error("sentinel配置反序列化失败:", e);
}
}
} catch (Exception e) {
log.error("读取redis错误", e);
}
return appNodes;
}
private void initCustomizedApis() {
if (Objects.isNull(commonAppLimit)) {
return;
}
if (commonAppLimit.getOpen()) {
Set<ApiDefinition> definitions = new HashSet<>();
ApiDefinition api = new ApiDefinition(commonAppLimit.getFlowRule());
HashSet<ApiPredicateItem> apiPredicateItems = new HashSet<>();
if (commonAppLimit.getUrls() != null && commonAppLimit.getUrls().size() > 0) {
for (String limitUrl : commonAppLimit.getUrls()) {
apiPredicateItems.add(new ApiPathPredicateItem().setPattern(limitUrl)
.setMatchStrategy(SentinelGatewayConstants.URL_MATCH_STRATEGY_EXACT));
}
}
if (commonAppLimit.getPreUrls() != null && commonAppLimit.getPreUrls().size() > 0) {
for (String limitUrl : commonAppLimit.getPreUrls()) {
apiPredicateItems.add(new ApiPathPredicateItem().setPattern(limitUrl)
.setMatchStrategy(SentinelGatewayConstants.URL_MATCH_STRATEGY_PREFIX));
}
}
api.setPredicateItems(apiPredicateItems);
definitions.add(api);
GatewayApiDefinitionManager.loadApiDefinitions(definitions);
}
log.info("initCustomizedApis方法的GatewayRuleManager:" + GatewayRuleManager.getRules().toString());
}
private void initGatewayRules() {
if (Objects.isNull(commonAppLimit)) {
return;
}
if (commonAppLimit.getOpen()) {
Set<GatewayFlowRule> rules = new HashSet<>();
rules.add(new GatewayFlowRule(commonAppLimit.getFlowRule())
.setResourceMode(SentinelGatewayConstants.RESOURCE_MODE_CUSTOM_API_NAME)
.setCount(commonAppLimit.getCount())
.setBurst(commonAppLimit.getBurst())
.setIntervalSec(commonAppLimit.getIntervalSec())
.setParamItem(new GatewayParamFlowItem()
.setParseStrategy(SentinelGatewayConstants.PARAM_PARSE_STRATEGY_HEADER)
.setFieldName("LimitRulesKey"))
);
GatewayRuleManager.loadRules(rules);
}
log.info("initCustomizedApis方法的GatewayRuleManager:" + GatewayRuleManager.getRules().toString());
}
private void initCustomizedApisFromRedis(List<AppNode> appNodes) {
Set<ApiDefinition> definitions = GatewayApiDefinitionManager.getApiDefinitions();
if (Objects.nonNull(appNodes) && appNodes.size() > 0) {
for (AppNode appNode : appNodes) {
ApiDefinition api = new ApiDefinition(appNode.getFlowRule());
if (Objects.nonNull(appNode.getUrls()) && appNode.getUrls().size() > 0) {
HashSet<ApiPredicateItem> apiPredicateItems = new HashSet<>();
for (String testUrl : appNode.getUrls()) {
apiPredicateItems.add(new ApiPathPredicateItem().setPattern(testUrl)
.setMatchStrategy(SentinelGatewayConstants.URL_MATCH_STRATEGY_PREFIX));
}
api.setPredicateItems(apiPredicateItems);
}
definitions.add(api);
}
}
GatewayApiDefinitionManager.loadApiDefinitions(definitions);
log.info("initCustomizedApisFromRedis方法的GatewayApiDefinitionManager:" + GatewayApiDefinitionManager.getApiDefinitions());
}
private void initGatewayRulesFromRedis(List<AppNode> appNodes) {
Set<GatewayFlowRule> rules = GatewayRuleManager.getRules();
if (Objects.nonNull(appNodes) && appNodes.size() > 0) {
for (AppNode appNode : appNodes) {
rules.add(new GatewayFlowRule(appNode.getFlowRule())
.setResourceMode(SentinelGatewayConstants.RESOURCE_MODE_CUSTOM_API_NAME)
.setCount(appNode.getCount())
.setBurst(appNode.getBurst())
.setIntervalSec(appNode.getIntervalSec())
.setParamItem(new GatewayParamFlowItem()
.setParseStrategy(SentinelGatewayConstants.PARAM_PARSE_STRATEGY_HEADER)
.setFieldName("LimitRulesKey")
.setMatchStrategy(SentinelGatewayConstants.PARAM_MATCH_STRATEGY_EXACT)
.setPattern(appNode.getCode())
)
);
}
}
GatewayRuleManager.loadRules(rules);
log.info("initGatewayRulesFromRedis方法的GatewayRuleManager:" + GatewayRuleManager.getRules().toString());
}
@Scheduled(fixedDelay = 30 * 60 * 1000, initialDelay = 30 * 60 * 1000)
public void loadSentinelConfig() {
log.info("=======执行loadSentinelConfig方法=========");
List<AppNode> appNodes = initPropertyFromRedis();
if (appNodes.size() == 0) {
return;
}
log.info("appNodes:" + appNodes);
initCustomizedApisFromRedis(appNodes);
initGatewayRulesFromRedis(appNodes);
}
}
自定义的handle
@Slf4j
public class JsonSentinelGatewayBlockExceptionHandler implements WebExceptionHandler {
private List<ViewResolver> viewResolvers;
private List<HttpMessageWriter<?>> messageWriters;
private final Supplier<ServerResponse.Context> contextSupplier = () -> {
return new ServerResponse.Context() {
public List<HttpMessageWriter<?>> messageWriters() {
return JsonSentinelGatewayBlockExceptionHandler.this.messageWriters;
}
public List<ViewResolver> viewResolvers() {
return JsonSentinelGatewayBlockExceptionHandler.this.viewResolvers;
}
};
};
public JsonSentinelGatewayBlockExceptionHandler(List<ViewResolver> viewResolvers, ServerCodecConfigurer serverCodecConfigurer) {
this.viewResolvers = viewResolvers;
this.messageWriters = serverCodecConfigurer.getWriters();
}
private Mono<Void> writeResponse(ServerResponse response, ServerWebExchange exchange) {
log.info("执行JsonSentinelGatewayBlockExceptionHandler的writeResponse方法");
ServerHttpResponse serverHttpResponse = exchange.getResponse();
serverHttpResponse.getHeaders().add("Content-Type", "application/json;charset=UTF-8");
JSONObject responseObj = new JSONObject();
responseObj.put("code", 444);
responseObj.put("message", "请求过于频繁,请稍后重试");
responseObj.put("data", null);
byte[] datas = responseObj.toString().getBytes(StandardCharsets.UTF_8);
DataBuffer buffer = serverHttpResponse.bufferFactory().wrap(datas);
return serverHttpResponse.writeWith(Mono.just(buffer));
}
public Mono<Void> handle(ServerWebExchange exchange, Throwable ex) {
if (exchange.getResponse().isCommitted()) {
return Mono.error(ex);
} else {
return !BlockException.isBlockException(ex) ? Mono.error(ex) : this.handleBlockedRequest(exchange, ex).flatMap((response) -> {
return this.writeResponse(response, exchange);
});
}
}
private Mono<ServerResponse> handleBlockedRequest(ServerWebExchange exchange, Throwable throwable) {
return GatewayCallbackManager.getBlockHandler().handleRequest(exchange, throwable);
}
}