sentinel支持以下相關(guān)功能邢锯,但是功能相應(yīng)的配置sentinel本身是基于內(nèi)存存儲(chǔ)的彼城,如果應(yīng)用服務(wù)重啟,則規(guī)則配置信息就會(huì)丟失候生,這種情況在使用方面非常的不方便,所以就要做規(guī)則配置的持久化操作绽昼;本片文章就是基于sentinel+nacos進(jìn)行規(guī)則配置持久化操作唯鸭,具體功能如下
- 流量控制
- 熔斷降級(jí)
- 系統(tǒng)自適應(yīng)保護(hù)
- 集群流量控制
- 網(wǎng)關(guān)流量控制
- 熱點(diǎn)參數(shù)限流
- 來(lái)源訪問(wèn)控制
由于sentinel的使用,大部分都是從官網(wǎng)下載sentinel的jar包硅确,然后啟動(dòng)即可訪問(wèn)控制臺(tái)目溉,但是添加規(guī)則配置持久化操作,則需要調(diào)整sentinel-dashboard部分源碼菱农,調(diào)整使用的持久化插件缭付。sentinel本身支持Apollo、Nacos循未、Zookeeper等插件進(jìn)行規(guī)則配置持久化工作陷猫。
基于Apollo的文章地址:Sentinel結(jié)合Apollo實(shí)現(xiàn)規(guī)則持久化功能
1. 下載sentinel的源碼,調(diào)整sentinel-dashboard部分源碼
sentinel源碼下載地址:https://github.com/alibaba 然后搜sentinel的妖,即可看到哦绣檬;到Tags里面下載對(duì)應(yīng)的版本即可。本文使用的是1.8.3版本
調(diào)整sentinel-dashboard部分代碼:
將test包下面的nacos文件夾復(fù)制粘貼到src-main-java下的rule目錄下嫂粟,操作如下圖:
工程最外層pom中的sentinel-datasource-nacos
依賴下面的scope
注釋或刪除掉
網(wǎng)關(guān)流控需要新建相應(yīng)的類娇未,包含添加GatewayFlowRuleNacosProvider
,GatewayFlowRuleNacosPublisher
兩個(gè)配置類和修改GatewayFlowRuleController
控制類,代碼如下:
GatewayFlowRuleNacosProvider.java
import com.alibaba.csp.sentinel.dashboard.datasource.entity.gateway.GatewayFlowRuleEntity;
import com.alibaba.csp.sentinel.dashboard.rule.DynamicRuleProvider;
import com.alibaba.csp.sentinel.datasource.Converter;
import com.alibaba.csp.sentinel.util.StringUtil;
import com.alibaba.nacos.api.config.ConfigService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.ArrayList;
import java.util.List;
@Component("gatewayFlowRuleNacosProvider")
public class GatewayFlowRuleNacosProvider implements DynamicRuleProvider<List<GatewayFlowRuleEntity>> {
@Autowired
private ConfigService configService;
@Autowired
private Converter<String, List<GatewayFlowRuleEntity>> converter;
@Override
public List<GatewayFlowRuleEntity> getRules(String appName) throws Exception {
String rules = configService.getConfig(appName + NacosConfigUtil.GATEWAY_FLOW_DATA_ID_POSTFIX,
NacosConfigUtil.GROUP_ID, 3000);
if (StringUtil.isEmpty(rules)) {
return new ArrayList<>();
}
return converter.convert(rules);
}
}
GatewayFlowRuleNacosPublisher.java
import com.alibaba.csp.sentinel.dashboard.datasource.entity.gateway.GatewayFlowRuleEntity;
import com.alibaba.csp.sentinel.dashboard.rule.DynamicRulePublisher;
import com.alibaba.csp.sentinel.datasource.Converter;
import com.alibaba.csp.sentinel.util.AssertUtil;
import com.alibaba.nacos.api.config.ConfigService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.util.List;
@Component("gatewayFlowRuleNacosPublisher")
public class GatewayFlowRuleNacosPublisher implements DynamicRulePublisher<List<GatewayFlowRuleEntity>> {
@Autowired
private ConfigService configService;
@Autowired
private Converter<List<GatewayFlowRuleEntity>, String> converter;
@Override
public void publish(String app, List<GatewayFlowRuleEntity> rules) throws Exception {
AssertUtil.notEmpty(app, "app name cannot be empty");
if (rules == null) {
return;
}
configService.publishConfig(app + NacosConfigUtil.GATEWAY_FLOW_DATA_ID_POSTFIX,
NacosConfigUtil.GROUP_ID, converter.convert(rules));
}
}
GatewayFlowRuleController.java
該類全部完整代碼如下: 具體修改位置如下圖“”
import com.alibaba.csp.sentinel.dashboard.auth.AuthAction;
import com.alibaba.csp.sentinel.dashboard.auth.AuthService;
import com.alibaba.csp.sentinel.dashboard.client.SentinelApiClient;
import com.alibaba.csp.sentinel.dashboard.datasource.entity.gateway.GatewayFlowRuleEntity;
import com.alibaba.csp.sentinel.dashboard.datasource.entity.gateway.GatewayParamFlowItemEntity;
import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.FlowRuleEntity;
import com.alibaba.csp.sentinel.dashboard.discovery.MachineInfo;
import com.alibaba.csp.sentinel.dashboard.domain.Result;
import com.alibaba.csp.sentinel.dashboard.domain.vo.gateway.rule.AddFlowRuleReqVo;
import com.alibaba.csp.sentinel.dashboard.domain.vo.gateway.rule.GatewayParamFlowItemVo;
import com.alibaba.csp.sentinel.dashboard.domain.vo.gateway.rule.UpdateFlowRuleReqVo;
import com.alibaba.csp.sentinel.dashboard.repository.gateway.InMemGatewayFlowRuleStore;
import com.alibaba.csp.sentinel.dashboard.rule.DynamicRuleProvider;
import com.alibaba.csp.sentinel.dashboard.rule.DynamicRulePublisher;
import com.alibaba.csp.sentinel.util.StringUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.web.bind.annotation.*;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import static com.alibaba.csp.sentinel.slots.block.RuleConstant.*;
import static com.alibaba.csp.sentinel.adapter.gateway.common.SentinelGatewayConstants.*;
import static com.alibaba.csp.sentinel.dashboard.datasource.entity.gateway.GatewayFlowRuleEntity.*;
/**
* Gateway flow rule Controller for manage gateway flow rules.
*
* @author cdfive
* @since 1.7.0
*/
@RestController
@RequestMapping(value = "/gateway/flow")
public class GatewayFlowRuleController {
private final Logger logger = LoggerFactory.getLogger(GatewayFlowRuleController.class);
@Autowired
private InMemGatewayFlowRuleStore repository;
// @Autowired
// private SentinelApiClient sentinelApiClient;
@Autowired
@Qualifier("gatewayFlowRuleNacosProvider")
private DynamicRuleProvider<List<GatewayFlowRuleEntity>> ruleProvider;
@Autowired
@Qualifier("gatewayFlowRuleNacosPublisher")
private DynamicRulePublisher<List<GatewayFlowRuleEntity>> rulePublisher;
@GetMapping("/list.json")
@AuthAction(AuthService.PrivilegeType.READ_RULE)
public Result<List<GatewayFlowRuleEntity>> queryFlowRules(String app, String ip, Integer port) {
if (StringUtil.isEmpty(app)) {
return Result.ofFail(-1, "app can't be null or empty");
}
if (StringUtil.isEmpty(ip)) {
return Result.ofFail(-1, "ip can't be null or empty");
}
if (port == null) {
return Result.ofFail(-1, "port can't be null");
}
try {
// List<GatewayFlowRuleEntity> rules = sentinelApiClient.fetchGatewayFlowRules(app, ip, port).get();
List<GatewayFlowRuleEntity> rules = ruleProvider.getRules(app);
repository.saveAll(rules);
return Result.ofSuccess(rules);
} catch (Throwable throwable) {
logger.error("query gateway flow rules error:", throwable);
return Result.ofThrowable(-1, throwable);
}
}
@PostMapping("/new.json")
@AuthAction(AuthService.PrivilegeType.WRITE_RULE)
public Result<GatewayFlowRuleEntity> addFlowRule(@RequestBody AddFlowRuleReqVo reqVo) {
String app = reqVo.getApp();
if (StringUtil.isBlank(app)) {
return Result.ofFail(-1, "app can't be null or empty");
}
GatewayFlowRuleEntity entity = new GatewayFlowRuleEntity();
entity.setApp(app.trim());
String ip = reqVo.getIp();
if (StringUtil.isBlank(ip)) {
return Result.ofFail(-1, "ip can't be null or empty");
}
entity.setIp(ip.trim());
Integer port = reqVo.getPort();
if (port == null) {
return Result.ofFail(-1, "port can't be null");
}
entity.setPort(port);
// API類型, Route ID或API分組
Integer resourceMode = reqVo.getResourceMode();
if (resourceMode == null) {
return Result.ofFail(-1, "resourceMode can't be null");
}
if (!Arrays.asList(RESOURCE_MODE_ROUTE_ID, RESOURCE_MODE_CUSTOM_API_NAME).contains(resourceMode)) {
return Result.ofFail(-1, "invalid resourceMode: " + resourceMode);
}
entity.setResourceMode(resourceMode);
// API名稱
String resource = reqVo.getResource();
if (StringUtil.isBlank(resource)) {
return Result.ofFail(-1, "resource can't be null or empty");
}
entity.setResource(resource.trim());
// 針對(duì)請(qǐng)求屬性
GatewayParamFlowItemVo paramItem = reqVo.getParamItem();
if (paramItem != null) {
GatewayParamFlowItemEntity itemEntity = new GatewayParamFlowItemEntity();
entity.setParamItem(itemEntity);
// 參數(shù)屬性 0-ClientIP 1-Remote Host 2-Header 3-URL參數(shù) 4-Cookie
Integer parseStrategy = paramItem.getParseStrategy();
if (!Arrays.asList(PARAM_PARSE_STRATEGY_CLIENT_IP, PARAM_PARSE_STRATEGY_HOST, PARAM_PARSE_STRATEGY_HEADER
, PARAM_PARSE_STRATEGY_URL_PARAM, PARAM_PARSE_STRATEGY_COOKIE).contains(parseStrategy)) {
return Result.ofFail(-1, "invalid parseStrategy: " + parseStrategy);
}
itemEntity.setParseStrategy(paramItem.getParseStrategy());
// 當(dāng)參數(shù)屬性為2-Header 3-URL參數(shù) 4-Cookie時(shí)星虹,參數(shù)名稱必填
if (Arrays.asList(PARAM_PARSE_STRATEGY_HEADER, PARAM_PARSE_STRATEGY_URL_PARAM, PARAM_PARSE_STRATEGY_COOKIE).contains(parseStrategy)) {
// 參數(shù)名稱
String fieldName = paramItem.getFieldName();
if (StringUtil.isBlank(fieldName)) {
return Result.ofFail(-1, "fieldName can't be null or empty");
}
itemEntity.setFieldName(paramItem.getFieldName());
}
String pattern = paramItem.getPattern();
// 如果匹配串不為空忘蟹,驗(yàn)證匹配模式
if (StringUtil.isNotEmpty(pattern)) {
itemEntity.setPattern(pattern);
Integer matchStrategy = paramItem.getMatchStrategy();
if (!Arrays.asList(PARAM_MATCH_STRATEGY_EXACT, PARAM_MATCH_STRATEGY_CONTAINS, PARAM_MATCH_STRATEGY_REGEX).contains(matchStrategy)) {
return Result.ofFail(-1, "invalid matchStrategy: " + matchStrategy);
}
itemEntity.setMatchStrategy(matchStrategy);
}
}
// 閾值類型 0-線程數(shù) 1-QPS
Integer grade = reqVo.getGrade();
if (grade == null) {
return Result.ofFail(-1, "grade can't be null");
}
if (!Arrays.asList(FLOW_GRADE_THREAD, FLOW_GRADE_QPS).contains(grade)) {
return Result.ofFail(-1, "invalid grade: " + grade);
}
entity.setGrade(grade);
// QPS閾值
Double count = reqVo.getCount();
if (count == null) {
return Result.ofFail(-1, "count can't be null");
}
if (count < 0) {
return Result.ofFail(-1, "count should be at lease zero");
}
entity.setCount(count);
// 間隔
Long interval = reqVo.getInterval();
if (interval == null) {
return Result.ofFail(-1, "interval can't be null");
}
if (interval <= 0) {
return Result.ofFail(-1, "interval should be greater than zero");
}
entity.setInterval(interval);
// 間隔單位
Integer intervalUnit = reqVo.getIntervalUnit();
if (intervalUnit == null) {
return Result.ofFail(-1, "intervalUnit can't be null");
}
if (!Arrays.asList(INTERVAL_UNIT_SECOND, INTERVAL_UNIT_MINUTE, INTERVAL_UNIT_HOUR, INTERVAL_UNIT_DAY).contains(intervalUnit)) {
return Result.ofFail(-1, "Invalid intervalUnit: " + intervalUnit);
}
entity.setIntervalUnit(intervalUnit);
// 流控方式 0-快速失敗 2-勻速排隊(duì)
Integer controlBehavior = reqVo.getControlBehavior();
if (controlBehavior == null) {
return Result.ofFail(-1, "controlBehavior can't be null");
}
if (!Arrays.asList(CONTROL_BEHAVIOR_DEFAULT, CONTROL_BEHAVIOR_RATE_LIMITER).contains(controlBehavior)) {
return Result.ofFail(-1, "invalid controlBehavior: " + controlBehavior);
}
entity.setControlBehavior(controlBehavior);
if (CONTROL_BEHAVIOR_DEFAULT == controlBehavior) {
// 0-快速失敗, 則Burst size必填
Integer burst = reqVo.getBurst();
if (burst == null) {
return Result.ofFail(-1, "burst can't be null");
}
if (burst < 0) {
return Result.ofFail(-1, "invalid burst: " + burst);
}
entity.setBurst(burst);
} else if (CONTROL_BEHAVIOR_RATE_LIMITER == controlBehavior) {
// 1-勻速排隊(duì), 則超時(shí)時(shí)間必填
Integer maxQueueingTimeoutMs = reqVo.getMaxQueueingTimeoutMs();
if (maxQueueingTimeoutMs == null) {
return Result.ofFail(-1, "maxQueueingTimeoutMs can't be null");
}
if (maxQueueingTimeoutMs < 0) {
return Result.ofFail(-1, "invalid maxQueueingTimeoutMs: " + maxQueueingTimeoutMs);
}
entity.setMaxQueueingTimeoutMs(maxQueueingTimeoutMs);
}
Date date = new Date();
entity.setGmtCreate(date);
entity.setGmtModified(date);
try {
entity = repository.save(entity);
} catch (Throwable throwable) {
logger.error("add gateway flow rule error:", throwable);
return Result.ofThrowable(-1, throwable);
}
// if (!publishRules(app, ip, port)) {
// logger.warn("publish gateway flow rules fail after add");
// }
try {
publishRules(app, ip, port);
} catch (Exception e) {
logger.warn("publish gateway flow rules fail after add",e);
}
return Result.ofSuccess(entity);
}
@PostMapping("/save.json")
@AuthAction(AuthService.PrivilegeType.WRITE_RULE)
public Result<GatewayFlowRuleEntity> updateFlowRule(@RequestBody UpdateFlowRuleReqVo reqVo) {
String app = reqVo.getApp();
if (StringUtil.isBlank(app)) {
return Result.ofFail(-1, "app can't be null or empty");
}
Long id = reqVo.getId();
if (id == null) {
return Result.ofFail(-1, "id can't be null");
}
GatewayFlowRuleEntity entity = repository.findById(id);
if (entity == null) {
return Result.ofFail(-1, "gateway flow rule does not exist, id=" + id);
}
// 針對(duì)請(qǐng)求屬性
GatewayParamFlowItemVo paramItem = reqVo.getParamItem();
if (paramItem != null) {
GatewayParamFlowItemEntity itemEntity = new GatewayParamFlowItemEntity();
entity.setParamItem(itemEntity);
// 參數(shù)屬性 0-ClientIP 1-Remote Host 2-Header 3-URL參數(shù) 4-Cookie
Integer parseStrategy = paramItem.getParseStrategy();
if (!Arrays.asList(PARAM_PARSE_STRATEGY_CLIENT_IP, PARAM_PARSE_STRATEGY_HOST, PARAM_PARSE_STRATEGY_HEADER
, PARAM_PARSE_STRATEGY_URL_PARAM, PARAM_PARSE_STRATEGY_COOKIE).contains(parseStrategy)) {
return Result.ofFail(-1, "invalid parseStrategy: " + parseStrategy);
}
itemEntity.setParseStrategy(paramItem.getParseStrategy());
// 當(dāng)參數(shù)屬性為2-Header 3-URL參數(shù) 4-Cookie時(shí)飒房,參數(shù)名稱必填
if (Arrays.asList(PARAM_PARSE_STRATEGY_HEADER, PARAM_PARSE_STRATEGY_URL_PARAM, PARAM_PARSE_STRATEGY_COOKIE).contains(parseStrategy)) {
// 參數(shù)名稱
String fieldName = paramItem.getFieldName();
if (StringUtil.isBlank(fieldName)) {
return Result.ofFail(-1, "fieldName can't be null or empty");
}
itemEntity.setFieldName(paramItem.getFieldName());
}
String pattern = paramItem.getPattern();
// 如果匹配串不為空,驗(yàn)證匹配模式
if (StringUtil.isNotEmpty(pattern)) {
itemEntity.setPattern(pattern);
Integer matchStrategy = paramItem.getMatchStrategy();
if (!Arrays.asList(PARAM_MATCH_STRATEGY_EXACT, PARAM_MATCH_STRATEGY_CONTAINS, PARAM_MATCH_STRATEGY_REGEX).contains(matchStrategy)) {
return Result.ofFail(-1, "invalid matchStrategy: " + matchStrategy);
}
itemEntity.setMatchStrategy(matchStrategy);
}
} else {
entity.setParamItem(null);
}
// 閾值類型 0-線程數(shù) 1-QPS
Integer grade = reqVo.getGrade();
if (grade == null) {
return Result.ofFail(-1, "grade can't be null");
}
if (!Arrays.asList(FLOW_GRADE_THREAD, FLOW_GRADE_QPS).contains(grade)) {
return Result.ofFail(-1, "invalid grade: " + grade);
}
entity.setGrade(grade);
// QPS閾值
Double count = reqVo.getCount();
if (count == null) {
return Result.ofFail(-1, "count can't be null");
}
if (count < 0) {
return Result.ofFail(-1, "count should be at lease zero");
}
entity.setCount(count);
// 間隔
Long interval = reqVo.getInterval();
if (interval == null) {
return Result.ofFail(-1, "interval can't be null");
}
if (interval <= 0) {
return Result.ofFail(-1, "interval should be greater than zero");
}
entity.setInterval(interval);
// 間隔單位
Integer intervalUnit = reqVo.getIntervalUnit();
if (intervalUnit == null) {
return Result.ofFail(-1, "intervalUnit can't be null");
}
if (!Arrays.asList(INTERVAL_UNIT_SECOND, INTERVAL_UNIT_MINUTE, INTERVAL_UNIT_HOUR, INTERVAL_UNIT_DAY).contains(intervalUnit)) {
return Result.ofFail(-1, "Invalid intervalUnit: " + intervalUnit);
}
entity.setIntervalUnit(intervalUnit);
// 流控方式 0-快速失敗 2-勻速排隊(duì)
Integer controlBehavior = reqVo.getControlBehavior();
if (controlBehavior == null) {
return Result.ofFail(-1, "controlBehavior can't be null");
}
if (!Arrays.asList(CONTROL_BEHAVIOR_DEFAULT, CONTROL_BEHAVIOR_RATE_LIMITER).contains(controlBehavior)) {
return Result.ofFail(-1, "invalid controlBehavior: " + controlBehavior);
}
entity.setControlBehavior(controlBehavior);
if (CONTROL_BEHAVIOR_DEFAULT == controlBehavior) {
// 0-快速失敗, 則Burst size必填
Integer burst = reqVo.getBurst();
if (burst == null) {
return Result.ofFail(-1, "burst can't be null");
}
if (burst < 0) {
return Result.ofFail(-1, "invalid burst: " + burst);
}
entity.setBurst(burst);
} else if (CONTROL_BEHAVIOR_RATE_LIMITER == controlBehavior) {
// 2-勻速排隊(duì), 則超時(shí)時(shí)間必填
Integer maxQueueingTimeoutMs = reqVo.getMaxQueueingTimeoutMs();
if (maxQueueingTimeoutMs == null) {
return Result.ofFail(-1, "maxQueueingTimeoutMs can't be null");
}
if (maxQueueingTimeoutMs < 0) {
return Result.ofFail(-1, "invalid maxQueueingTimeoutMs: " + maxQueueingTimeoutMs);
}
entity.setMaxQueueingTimeoutMs(maxQueueingTimeoutMs);
}
Date date = new Date();
entity.setGmtModified(date);
try {
entity = repository.save(entity);
} catch (Throwable throwable) {
logger.error("update gateway flow rule error:", throwable);
return Result.ofThrowable(-1, throwable);
}
// if (!publishRules(app, entity.getIp(), entity.getPort())) {
// logger.warn("publish gateway flow rules fail after update");
// }
try {
publishRules(app, entity.getIp(), entity.getPort());
} catch (Exception e) {
logger.warn("publish gateway flow rules fail after update",e);
}
return Result.ofSuccess(entity);
}
@PostMapping("/delete.json")
@AuthAction(AuthService.PrivilegeType.DELETE_RULE)
public Result<Long> deleteFlowRule(Long id) {
if (id == null) {
return Result.ofFail(-1, "id can't be null");
}
GatewayFlowRuleEntity oldEntity = repository.findById(id);
if (oldEntity == null) {
return Result.ofSuccess(null);
}
try {
repository.delete(id);
} catch (Throwable throwable) {
logger.error("delete gateway flow rule error:", throwable);
return Result.ofThrowable(-1, throwable);
}
// if (!publishRules(oldEntity.getApp(), oldEntity.getIp(), oldEntity.getPort())) {
// logger.warn("publish gateway flow rules fail after delete");
// }
try {
publishRules(oldEntity.getApp(), oldEntity.getIp(), oldEntity.getPort());
} catch (Exception e) {
logger.warn("publish gateway flow rules fail after delete",e);
}
return Result.ofSuccess(id);
}
private void publishRules(String app, String ip, Integer port) throws Exception{
List<GatewayFlowRuleEntity> rules = repository.findAllByMachine(MachineInfo.of(app, ip, port));
rulePublisher.publish(app, rules);
// return sentinelApiClient.modifyGatewayFlowRules(app, ip, port, rules);
}
}
調(diào)整NacosConfig.java
配置類
import com.alibaba.csp.sentinel.dashboard.datasource.entity.gateway.GatewayFlowRuleEntity;
import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.FlowRuleEntity;
import com.alibaba.csp.sentinel.datasource.Converter;
import com.alibaba.fastjson.JSON;
import com.alibaba.nacos.api.PropertyKeyConst;
import com.alibaba.nacos.api.config.ConfigFactory;
import com.alibaba.nacos.api.config.ConfigService;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.List;
import java.util.Properties;
/**
* @author Eric Zhao
* @since 1.4.0
*/
@Configuration
public class NacosConfig {
@Value("${naocs.address}")
private String address;
@Value("${nacos.namespace}")
private String namespace;
@Value("${nacos.clusterName}")
private String clusterName;
@Bean
public Converter<List<FlowRuleEntity>, String> flowRuleEntityEncoder() {
return JSON::toJSONString;
}
@Bean
public Converter<String, List<FlowRuleEntity>> flowRuleEntityDecoder() {
return s -> JSON.parseArray(s, FlowRuleEntity.class);
}
@Bean
public ConfigService nacosConfigService() throws Exception {
Properties properties = new Properties(); //定義nacos的配置屬性
properties.put(PropertyKeyConst.SERVER_ADDR,this.address);
properties.put(PropertyKeyConst.NAMESPACE,this.namespace);
properties.put(PropertyKeyConst.CLUSTER_NAME,this.clusterName);
return ConfigFactory.createConfigService(properties);
// return ConfigFactory.createConfigService("127.0.0.1:8082");
}
@Bean
public Converter<List<GatewayFlowRuleEntity>, String> gatewayFlowRuleEntityEncoder() {
return JSON::toJSONString;
}
@Bean
public Converter<String, List<GatewayFlowRuleEntity>> gatewayFlowRuleEntityDecoder() {
return s -> JSON.parseArray(s, GatewayFlowRuleEntity.class);
}
}
調(diào)整FlowControllerV2.java
類加載配置
NacosConfigUtil.java
添加常量值如下:
public static final String GATEWAY_FLOW_DATA_ID_POSTFIX = "-gateway-flow-rules";
application.properties
配置文件
naocs.address=127.0.0.1:8082
nacos.namespace=Dev
nacos.clusterName=
# 如果SpringBoot版本自行升級(jí)到2.5.X以上版本媚值,則需要加下面這一行配置狠毯,解決版本升級(jí)后的雙斜杠導(dǎo)致的404問(wèn)題
spring.mvc.pathmatch.matching-strategy = ant_path_matcher
通過(guò)以上調(diào)整,sentinel-dashboard就調(diào)整完了褥芒,打包進(jìn)行部署啟動(dòng)即可=浪伞!锰扶!
2. 應(yīng)用服務(wù)添加配置及nacos配置中心添加的配置
pom依賴配置
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
</dependency>
yml文件配置
spring:
application.name: SentinelService
cloud:
sentinel:
log:
dir: /logs
switch-pid: true
transport:
dashboard: 127.0.0.1:8080
datasource:
flow: #流控規(guī)則
nacos:
namespace: Dev
server-addr: 127.0.0.1:8082
dataId: ${spring.application.name}-flow-rules
groupId: SENTINEL_GROUP
data-type: json
rule-type: flow
測(cè)試接口如下:
@RestController
public class TestController {
@GetMapping(value = "/get")
public DataResult getMethod(){
DataResult<String> dataResult = new DataResult<>();
dataResult.setCode(HttpStatus.OK.value());
dataResult.setData("Success");
return dataResult;
}
}
nacos配置
在nacos上面需要?jiǎng)?chuàng)建命名空間献酗,新增配置等操作;
配置命名空間是我之前創(chuàng)建好的坷牛,我主要用來(lái)區(qū)分環(huán)境罕偎,所以我這里就直接使用了Dev命名空間,如上述配置
新增配置SentinelService-flow-rules
京闰,配置DataId的規(guī)則是"服務(wù)名-flow-rules"颜及,配置的內(nèi)容如下:
[{
"resource": "/get",
"limitApp": "default",
"grade": 1,
"count": 2,
"clusterMode": false,
"controlBehavior": 0,
"strategy": 0,
"warmUpPeriodSec": 10,
"maxQueueingTimeMs": 500,
"refResource": "rrr"
}]
通過(guò)上述配置,就完成了sentinel+nacos流量控制規(guī)則的持久化工作蹂楣,啟動(dòng)應(yīng)用服務(wù)俏站,多次訪問(wèn)/get接口,大約10秒左右痊土,就可以看到sentinel控制臺(tái)上的數(shù)據(jù)了肄扎,其中流量控制頁(yè)面就會(huì)出現(xiàn)nacos中的配置數(shù)據(jù)。結(jié)果參考下圖:
以上只提供了流控規(guī)則的相關(guān)json及yml配置數(shù)據(jù)赁酝,下面提供一下其他規(guī)則的json配置及yml配置犯祠,以及相關(guān)的參數(shù)說(shuō)明
json參數(shù)是配置nacos使用的,但是提供的json串中包含注釋酌呆,使用時(shí)需要將注釋刪掉雷则,保證json串的正確性
流控規(guī)則
[
{
// 資源名
"resource": "/test",
// 針對(duì)來(lái)源,若為 default 則不區(qū)分調(diào)用來(lái)源
"limitApp": "default",
// 限流閾值類型(1:QPS;0:并發(fā)線程數(shù))
"grade": 1,
// 閾值
"count": 1,
// 是否是集群模式
"clusterMode": false,
// 流控效果(0:快速失敗;1:Warm Up(預(yù)熱模式);2:排隊(duì)等待)
"controlBehavior": 0,
// 流控模式(0:直接肪笋;1:關(guān)聯(lián);2:鏈路)
"strategy": 0,
// 預(yù)熱時(shí)間(秒月劈,預(yù)熱模式需要此參數(shù))
"warmUpPeriodSec": 10,
// 超時(shí)時(shí)間(排隊(duì)等待模式需要此參數(shù))
"maxQueueingTimeMs": 500,
// 關(guān)聯(lián)資源、入口資源(關(guān)聯(lián)藤乙、鏈路模式)
"refResource": "rrr"
}
]
熔斷規(guī)則
[
{
// 資源名
"resource": "/test1",
"limitApp": "default",
// 熔斷策略(0:慢調(diào)用比例猜揪,1:異常比率,2:異常計(jì)數(shù))
"grade": 0,
// 最大RT坛梁、比例閾值而姐、異常數(shù)
"count": 200,
// 慢調(diào)用比例閾值,僅慢調(diào)用比例模式有效(1.8.0 引入)
"slowRatioThreshold": 0.2,
// 最小請(qǐng)求數(shù)
"minRequestAmount": 5,
// 當(dāng)單位統(tǒng)計(jì)時(shí)長(zhǎng)(類中默認(rèn)1000)
"statIntervalMs": 1000,
// 熔斷時(shí)長(zhǎng)
"timeWindow": 10
}
]
熱點(diǎn)規(guī)則
[
{
// 資源名
"resource": "/test1",
// 限流模式(QPS 模式划咐,不可更改)
"grade": 1,
// 參數(shù)索引
"paramIdx": 0,
// 單機(jī)閾值
"count": 13,
// 統(tǒng)計(jì)窗口時(shí)長(zhǎng)
"durationInSec": 6,
// 是否集群 默認(rèn)false
"clusterMode": false,
//
"burstCount": 0,
// 集群模式配置
"clusterConfig": {
//
"fallbackToLocalWhenFail": true,
//
"flowId": 2,
//
"sampleCount": 10,
//
"thresholdType": 0,
//
"windowIntervalMs": 1000
},
// 流控效果(支持快速失敗和勻速排隊(duì)模式)
"controlBehavior": 0,
//
"limitApp": "default",
//
"maxQueueingTimeMs": 0,
// 高級(jí)選項(xiàng)
"paramFlowItemList": [
{
// 參數(shù)類型
"classType": "int",
// 限流閾值
"count": 222,
// 參數(shù)值
"object": "2"
}
]
}
]
系統(tǒng)規(guī)則
[
{
// RT
"avgRt": 1,
// CPU 使用率
"highestCpuUsage": -1,
// LOAD
"highestSystemLoad": -1,
// 線程數(shù)
"maxThread": -1,
// 入口 QPS
"qps": -1
}
]
授權(quán)規(guī)則
[
{
// 資源名
"resource": "sentinel_spring_web_context",
// 流控應(yīng)用
"limitApp": "/test",
// 授權(quán)類型(0代表白名單拴念;1代表黑名單钧萍。)
"strategy": 0
}
]
各類規(guī)則對(duì)應(yīng)的yml配置
spring:
application.name: SentinelService
cloud:
nacos:
discovery:
server-addr: 127.0.0.1:8082 #Nacos 服務(wù)地址
sentinel:
log:
dir: /logs
switch-pid: true
transport:
dashboard: 127.0.0.1:8080
datasource:
flow: #流控規(guī)則
nacos:
namespace: Dev
server-addr: 127.0.0.1:8082
dataId: ${spring.application.name}-flow-rules
groupId: SENTINEL_GROUP
data-type: json
rule-type: flow
degrade: #熔斷規(guī)則
nacos:
namespace: Dev
server-addr: 127.0.0.1:8082
dataId: ${spring.application.name}-degrade-rules
groupId: SENTINEL_GROUP
data-type: json
rule-type: degrade
system: #系統(tǒng)規(guī)則
nacos:
namespace: Dev
server-addr: 127.0.0.1:8082
dataId: ${spring.application.name}-system-rules
groupId: SENTINEL_GROUP
data-type: json
rule-type: system
authority: #授權(quán)規(guī)則
nacos:
namespace: Dev
server-addr: 127.0.0.1:8082
dataId: ${spring.application.name}-authority-rules
groupId: SENTINEL_GROUP
data-type: json
rule-type: authority
param-flow: #熱點(diǎn)參數(shù)規(guī)則
nacos:
namespace: Dev
server-addr: 127.0.0.1:8082
dataId: ${spring.application.name}-param-flow-rules
groupId: SENTINEL_GROUP
data-type: json
rule-type: param-flow
以上提供了五種規(guī)則的nacos配置及yml配置,可以完整的支持sentinel規(guī)則配置持久化功能政鼠!
如有問(wèn)題歡迎評(píng)論留言溝通风瘦!