Sentinel的理念是只需要開(kāi)發(fā)者關(guān)注資源的定義,它默認(rèn)會(huì)對(duì)資源進(jìn)行流控。當(dāng)然,我們還是需要對(duì)定義的資源設(shè)置流控規(guī)則,主要有兩種方式:
- 通過(guò)FlowRuleManager.loadRules()手動(dòng)加載流控規(guī)則。
- 在Sentinel Dashboard上針對(duì)資源動(dòng)態(tài)創(chuàng)建流控規(guī)則。
針對(duì)第一種方式,如果接入Sentinel Dashboard,那么同樣支持動(dòng)態(tài)修改流控規(guī)則,但是基于Sentinel Dashboard所配置的流控規(guī)則,都是保存在內(nèi)存中的,一旦應(yīng)用重啟,這些規(guī)則都會(huì)被清除。為了解決這個(gè)問(wèn)題,Sentinel提供了動(dòng)態(tài)數(shù)據(jù)源支持。
目前,Sentinel支持Consul、Zookeeper、Redis、Nacos、Apollo、etcd等數(shù)據(jù)源的擴(kuò)展,接下來(lái)通過(guò)一個(gè)案例展示Spring Cloud Sentinel集成Nacos實(shí)現(xiàn)動(dòng)態(tài)流控規(guī)則,步驟如下:
- 添加Nacos數(shù)據(jù)源的依賴(lài)包
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
<version>1.7.0</version>
</dependency>
- 創(chuàng)建一個(gè)REST接口,用于測(cè)試
@RestController
public class DynamicController {
@GetMapping("/dynamic")
public String dynamic() {
return "Hello Dynamic Rules";
}
}
- 在application.yml文件中添加數(shù)據(jù)源配置
spring:
application:
name: sentinel-spring-cloud-demo
cloud:
sentinel:
transport:
dashboard: 192.168.56.1:7777
datasource:
- nacos:
server-addr: 192.168.56.1:8848
data-id: ${spring.application.name}-sentinel
group-id: DEFAULT_GROUP
data-type: json
rule-type: flow
部分配置說(shuō)明如下:
- datasource: 目前支持redis、apollo、zk、file、nacos,選什么類(lèi)型的數(shù)據(jù)源就配置相應(yīng)的key即可。
- data-id:可以設(shè)置成${spring.application.name},方便區(qū)分不同應(yīng)用的配置。
- rule-type:表示數(shù)據(jù)源中規(guī)則屬于哪種類(lèi)型,如flow、degrade、param-flow、gw-flow等。
- data-type:指配置項(xiàng)的內(nèi)容格式,Spring Cloud Alibaba Sentinel提供了JSON和XML兩種格式,如需要自定義,則可以將值配置為custom,并配置converter-class指向converter類(lèi)。
-
登陸Nacos控制臺(tái),創(chuàng)建流控配置規(guī)則:
在這里插入圖片描述 -
登陸Sentinel Dashboard,找到執(zhí)行項(xiàng)目名稱(chēng)菜單下的“流控規(guī)則”,就可以看到在Nacos上所配置的流控規(guī)則已經(jīng)被加載了。
在這里插入圖片描述 - 當(dāng)我們?cè)贜acos的控制臺(tái)上修改流控規(guī)則后,可以同步的在Sentinel Dashboard上看到流控規(guī)則的變化。
那么問(wèn)題就來(lái)了,Nacos其實(shí)應(yīng)該作為一個(gè)流控規(guī)則的持久化平臺(tái),正常的操作過(guò)程應(yīng)該是在Sentinel Dashboard上修改流控規(guī)則,然后同步到Nacos上,但是遺憾的是,目前Sentinel Dashboard并不支持該功能。
所以,Nacos名義上是"Datasource",實(shí)際上充當(dāng)?shù)娜匀皇桥渲弥行牡慕巧_(kāi)發(fā)者可以在Nacos控制臺(tái)上動(dòng)態(tài)修改流控規(guī)則并實(shí)現(xiàn)規(guī)則同步。在實(shí)際開(kāi)發(fā)中,很難避免在不清楚情況的情況下,部分開(kāi)發(fā)者使用Sentinel Dashboard來(lái)管理流控規(guī)則,部分開(kāi)發(fā)者通過(guò)Nacos來(lái)管理流控規(guī)則,這可能導(dǎo)致非常嚴(yán)重的問(wèn)題。
要想使用Sentinel Dashboard來(lái)統(tǒng)一管理流控規(guī)則并同步到Nacos上,我們可以自己來(lái)實(shí)現(xiàn)。
Sentinel Dashboard集成Nacos實(shí)現(xiàn)規(guī)則同步
Sentinel Dashboard的流控規(guī)則下的所有操作,都會(huì)調(diào)用Sentinel-Dashboard源碼中的FlowControllerV1類(lèi),這個(gè)類(lèi)中包含流控規(guī)則本地化 的CRUD操作。
另外,在com.alibaba.csp.sentinel.dashboard.controller.v2包下存在一個(gè)FlowControllerV2類(lèi),這個(gè)類(lèi)同樣提供流控規(guī)則的CRUD,和V1版本不同的是,它可以實(shí)現(xiàn)指定數(shù)據(jù)源的規(guī)則拉取和發(fā)布。
@RestController
@RequestMapping(value = "/v2/flow")
public class FlowControllerV2 {
private final Logger logger = LoggerFactory.getLogger(FlowControllerV2.class);
@Autowired
private InMemoryRuleRepositoryAdapter<FlowRuleEntity> repository;
@Autowired
@Qualifier("flowRuleDefaultProvider")
private DynamicRuleProvider<List<FlowRuleEntity>> ruleProvider;
@Autowired
@Qualifier("flowRuleDefaultPublisher")
private DynamicRulePublisher<List<FlowRuleEntity>> rulePublisher;
FlowControllerV2依賴(lài)以下兩個(gè)非常重要的類(lèi):
- DynamicRuleProvider: 動(dòng)態(tài)規(guī)則的拉取,從指定數(shù)據(jù)源中獲取流控規(guī)則后在Sentinel Dashboard中展示。
- DynamicRulePublisher: 動(dòng)態(tài)規(guī)則的發(fā)布,將在Sentinel Dashboard中修改的規(guī)則同步到指定數(shù)據(jù)源中。
我們可以擴(kuò)展這兩個(gè)類(lèi),然后集成Nacos來(lái)實(shí)現(xiàn)Sentinel Dashboard規(guī)則的同步。
Sentinel Dashboard源碼修改
修改Sentinel Dashboard的源碼,具體實(shí)現(xiàn)步驟如下:
- 在GitHub中下載Sentinel Dashboard 1.7.1的源碼。
- 使用IDEA工具打開(kāi)sentinel-dashboard工程。
- 在pom.xml中把sentinel-datasource-nacos依賴(lài)的<scope>注釋掉。
<!-- for Nacos rule publisher sample -->
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
<!--<scope>test</scope>-->
</dependency>
- 修改resources/app/scripts/directives/sidebar/sidebar.html文件中下面這段代碼,將dashboard.flowV1改成dashboard.flow,也就是去掉V1。修改之后,會(huì)調(diào)用FlowControllerV2中的接口。
<li ui-sref-active="active" ng-if="!entry.isGateway">
<!--<a ui-sref="dashboard.flowV1({app: entry.app})">-->
<a ui-sref="dashboard.flow({app: entry.app})">
<i class="glyphicon glyphicon-filter"></i> 流控規(guī)則</a>
</li>
- 在com.alibaba.csp.sentinel.dashboard.rule包中創(chuàng)建一個(gè)Nacos包,并創(chuàng)建一個(gè)類(lèi)用來(lái)加載外部化配置。
@ConfigurationProperties(prefix = "sentinel.nacos")
public class NacosPropertiesConfiguration {
private String serverAddr;
private String dataId;
private String groupId = "DEFAULT_GROUP";
private String namespace;
// 省略get/set方法
}
- 創(chuàng)建一個(gè)Nacos配置類(lèi)NacosConfiguration
@EnableConfigurationProperties(NacosPropertiesConfiguration.class)
@Configuration
public class NacosConfiguration {
@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(NacosPropertiesConfiguration nacosPropertiesConfiguration) throws NacosException {
Properties properties = new Properties();
properties.put(PropertyKeyConst.SERVER_ADDR, nacosPropertiesConfiguration.getServerAddr());
properties.put(PropertyKeyConst.NAMESPACE, nacosPropertiesConfiguration.getNamespace());
return ConfigFactory.createConfigService(properties);
}
}
- 創(chuàng)建一個(gè)常量類(lèi)NacosConstants,分別表示默認(rèn)的GROUP_ID和DATA_ID的后綴
public class NacosConstants {
public static final String DATA_ID_POSTFIX = "-sentinel-flow";
public static final String GROUP_ID = "DEFAULT_GROUP";
}
- 實(shí)現(xiàn)動(dòng)態(tài)從Nacos配置中心獲取流控規(guī)則
@Service
public class FlowRuleNacosProvider implements DynamicRuleProvider<List<FlowRuleEntity>> {
private static final Logger logger = LoggerFactory.getLogger(FlowRuleNacosProvider.class);
@Autowired
private NacosPropertiesConfiguration nacosPropertiesConfiguration;
@Autowired
private ConfigService configService;
@Autowired
private Converter<String, List<FlowRuleEntity>> converter;
@Override
public List<FlowRuleEntity> getRules(String appName) throws Exception {
String dataId = new StringBuilder(appName).append(NacosConstants.DATA_ID_POSTFIX).toString();
String rules = configService.getConfig(dataId, nacosPropertiesConfiguration.getGroupId(), 3000);
logger.info("pull flow rule from Nacos Config: {}", rules);
if (StringUtils.isEmpty(rules)) {
return new ArrayList<>();
}
return converter.convert(rules);
}
}
- 創(chuàng)建一個(gè)流控規(guī)則發(fā)布類(lèi),在Sentinel Dashboard上修改完配置后,需要調(diào)用該發(fā)布方法將數(shù)據(jù)持久化到Nacos中。
@Service
public class FlowRuleNacosPublisher implements DynamicRulePublisher<List<FlowRuleEntity>> {
@Autowired
private NacosPropertiesConfiguration nacosPropertiesConfiguration;
@Autowired
private ConfigService configService;
@Autowired
private Converter<List<FlowRuleEntity>, String> converter;
@Override
public void publish(String app, List<FlowRuleEntity> rules) throws Exception {
AssertUtil.notEmpty(app, "app cannot be empty");
if (rules == null) {
return;
}
String dataId = new StringBuilder(app).append(NacosConstants.DATA_ID_POSTFIX).toString();
configService.publishConfig(dataId, nacosPropertiesConfiguration.getGroupId(), converter.convert(rules));
}
}
- 修改FlowControllerV2類(lèi),將上面配置的兩個(gè)類(lèi)注入進(jìn)來(lái),表示規(guī)則的拉取和規(guī)則的發(fā)布統(tǒng)一用我們前面定義的兩個(gè)實(shí)例,然后將FlowControllerV2這個(gè)類(lèi)中的代碼覆蓋FlowControllerV1的代碼
@RestController
@RequestMapping(value = "/v2/flow")
public class FlowControllerV2 {
private final Logger logger = LoggerFactory.getLogger(FlowControllerV2.class);
@Autowired
private InMemoryRuleRepositoryAdapter<FlowRuleEntity> repository;
@Autowired
@Qualifier("flowRuleNacosProvider")
private DynamicRuleProvider<List<FlowRuleEntity>> ruleProvider;
@Autowired
@Qualifier("flowRuleNacosPublisher")
private DynamicRulePublisher<List<FlowRuleEntity>> rulePublisher;
- 在application.properties文件中添加Nacos服務(wù)端的配置信息
sentinel.nacos.serverAddr=192.168.56.1:8848
sentinel.nacos.namespacec=
sentinel.nacos.group-id=DEFAULT_GROUP
- 使用下面命令將代碼打包成一個(gè) fat jar,然后啟動(dòng)。
mvn clean package
Sentinel Dashboard規(guī)則數(shù)據(jù)同步
對(duì)于應(yīng)用程序來(lái)說(shuō),需要改動(dòng)的地方比較少,只需要注意配置文件中的data-id的命名要以-sentinel-flow結(jié)尾即可,因?yàn)樵趕entinel dashboard中我們寫(xiě)了一個(gè)固定的后綴。
spring:
application:
name: spring-cloud-sentinel-dynamic
cloud:
sentinel:
transport:
dashboard: 192.168.56.1:7777
datasource:
- nacos:
server-addr: 192.168.56.1:8848
data-id: ${spring.application.name}-sentinel-flow
group-id: DEFAULT_GROUP
data-type: json
rule-type: flow
- 登陸Sentinel Dashboard,進(jìn)入“流控規(guī)則”,然后針對(duì)指定的資源創(chuàng)建流控規(guī)則。
-
進(jìn)入Nacos控制臺(tái),就可以看到在Sentinel Dashboard中配置的流控規(guī)則。
在這里插入圖片描述