配置說(shuō)明參考: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>
需求:對(duì)不同的應(yīng)用進(jìn)行不一樣的限流策略,并且對(duì)特定接口進(jìn)行通用限流
實(shí)現(xiàn)方法:不同應(yīng)用發(fā)送的請(qǐng)求時(shí)揽碘,由于項(xiàng)目的攔截器在sentinel的攔截器之前求厕,我會(huì)攔截所有的請(qǐng)求技竟,在請(qǐng)求頭中設(shè)置key為L(zhǎng)imitRulesKey苛秕,value為應(yīng)用code的鍵值對(duì)粉寞。由于項(xiàng)目初始化后奈梳,會(huì)讀取配置配置在redis中的各個(gè)應(yīng)用的限流策略并加載到sentinel中,sentinel會(huì)對(duì)匹配中攔截規(guī)則(例如正則匹配矢门,前綴匹配等)的請(qǐng)求中獲取請(qǐng)求的LimitRulesKey鍵值對(duì),拿redis中的應(yīng)用的code與LimitRulesKey鍵值對(duì)中的value進(jìn)行比較灰蛙,相等就進(jìn)行此應(yīng)用code對(duì)應(yīng)的限流策略祟剔,不相等就進(jìn)行配置接口的通用限流。sentinel每30分鐘讀取一次redis的應(yīng)用限流策略摩梧,更新已配置的限流策略
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("=========執(zhí)行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錯(cuò)誤", 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("=======執(zhí)行l(wèi)oadSentinelConfig方法=========");
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("執(zhí)行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", "請(qǐng)求過(guò)于頻繁物延,請(qǐng)稍后重試");
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);
}
}