Sentinel的理念是只需要开发者关注资源的定义,它默认会对资源进行流控。当然,我们还是需要对定义的资源设置流控规则,主要有两种方式:
- 通过FlowRuleManager.loadRules()手动加载流控规则。
- 在Sentinel Dashboard上针对资源动态创建流控规则。
针对第一种方式,如果接入Sentinel Dashboard,那么同样支持动态修改流控规则,但是基于Sentinel Dashboard所配置的流控规则,都是保存在内存中的,一旦应用重启,这些规则都会被清除。为了解决这个问题,Sentinel提供了动态数据源支持。
目前,Sentinel支持Consul、Zookeeper、Redis、Nacos、Apollo、etcd等数据源的扩展,接下来通过一个案例展示Spring Cloud Sentinel集成Nacos实现动态流控规则,步骤如下:
1. 添加Nacos数据源的依赖包
<dependency> <groupId>com.alibaba.cspgroupId> <artifactId>sentinel-datasource-nacosartifactId> <version>1.7.0version>dependency>
2. 创建一个REST接口,用于测试
@RestControllerpublic class DynamicController { @GetMapping("/dynamic") public String dynamic() { return "Hello Dynamic Rules"; }}
3. 在application.yml文件中添加数据源配置
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
部分配置说明如下:
- datasource: 目前支持redis、apollo、zk、file、nacos,选什么类型的数据源就配置相应的key即可。
- data-id:可以设置成${spring.application.name},方便区分不同应用的配置。
- rule-type:表示数据源中规则属于哪种类型,如flow、degrade、param-flow、gw-flow等。
- data-type:指配置项的内容格式,Spring Cloud Alibaba Sentinel提供了JSON和XML两种格式,如需要自定义,则可以将值配置为custom,并配置converter-class指向converter类。
4. 登陆Nacos控制台,创建流控配置规则:
5. 登陆Sentinel Dashboard,找到执行项目名称菜单下的“流控规则”,就可以看到在Nacos上所配置的流控规则已经被加载了。
6. 当我们在Nacos的控制台上修改流控规则后,可以同步的在Sentinel Dashboard上看到流控规则的变化。
那么问题就来了,Nacos其实应该作为一个流控规则的持久化平台,正常的操作过程应该是在Sentinel Dashboard上修改流控规则,然后同步到Nacos上,但是遗憾的是,目前Sentinel Dashboard并不支持该功能。
所以,Nacos名义上是"Datasource",实际上充当的仍然是配置中心的角色,开发者可以在Nacos控制台上动态修改流控规则并实现规则同步。在实际开发中,很难避免在不清楚情况的情况下,部分开发者使用Sentinel Dashboard来管理流控规则,部分开发者通过Nacos来管理流控规则,这可能导致非常严重的问题。
要想使用Sentinel Dashboard来统一管理流控规则并同步到Nacos上,我们可以自己来实现。
Sentinel Dashboard集成Nacos实现规则同步
Sentinel Dashboard的流控规则下的所有操作,都会调用Sentinel-Dashboard源码中的FlowControllerV1类,这个类中包含流控规则本地化 的CRUD操作。
另外,在com.alibaba.csp.sentinel.dashboard.controller.v2包下存在一个FlowControllerV2类,这个类同样提供流控规则的CRUD,和V1版本不同的是,它可以实现指定数据源的规则拉取和发布。
@RestController@RequestMapping(value = "/v2/flow")public class FlowControllerV2 { private final Logger logger = LoggerFactory.getLogger(FlowControllerV2.class); @Autowired private InMemoryRuleRepositoryAdapter repository; @Autowired @Qualifier("flowRuleDefaultProvider") private DynamicRuleProvider> ruleProvider; @Autowired @Qualifier("flowRuleDefaultPublisher") private DynamicRulePublisher> rulePublisher;
FlowControllerV2依赖以下两个非常重要的类:
- DynamicRuleProvider: 动态规则的拉取,从指定数据源中获取流控规则后在Sentinel Dashboard中展示。
- DynamicRulePublisher: 动态规则的发布,将在Sentinel Dashboard中修改的规则同步到指定数据源中。
我们可以扩展这两个类,然后集成Nacos来实现Sentinel Dashboard规则的同步。
Sentinel Dashboard源码修改
修改Sentinel Dashboard的源码,具体实现步骤如下:
1. 在GitHub中下载Sentinel Dashboard 1.7.1的源码。
2. 使用IDEA工具打开sentinel-dashboard工程。
3. 在pom.xml中把sentinel-datasource-nacos依赖的注释掉。
<dependency> <groupId>com.alibaba.cspgroupId> <artifactId>sentinel-datasource-nacosartifactId> dependency>
4. 修改resources/app/scripts/directives/sidebar/sidebar.html文件中下面这段代码,将dashboard.flowV1改成dashboard.flow,也就是去掉V1。修改之后,会调用FlowControllerV2中的接口。
"active" ng- <a ui-sref="dashboard.flow({app: entry.app})"> <i class="glyphicon glyphicon-filter">i> 流控规则a>li>
5. 在com.alibaba.csp.sentinel.dashboard.rule包中创建一个Nacos包,并创建一个类用来加载外部化配置。
@ConfigurationProperties(prefix = "sentinel.nacos")public class NacosPropertiesConfiguration { private String serverAddr; private String dataId; private String groupId = "DEFAULT_GROUP"; private String namespace; // 省略get/set方法}
6. 创建一个Nacos配置类NacosConfiguration
@EnableConfigurationProperties(NacosPropertiesConfiguration.class)@Configurationpublic class NacosConfiguration { @Bean public Converter, String> flowRuleEntityEncoder() { return JSON::toJSONString; } @Bean public Converter> 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); }}
7. 创建一个常量类NacosConstants,分别表示默认的GROUP_ID和DATA_ID的后缀
public class NacosConstants { public static final String DATA_ID_POSTFIX = "-sentinel-flow"; public static final String GROUP_ID = "DEFAULT_GROUP";}
8. 实现动态从Nacos配置中心获取流控规则
@Servicepublic 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> converter; @Override public ListgetRules(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); }}
9. 创建一个流控规则发布类,在Sentinel Dashboard上修改完配置后,需要调用该发布方法将数据持久化到Nacos中。
@Servicepublic class FlowRuleNacosPublisher implements DynamicRulePublisher<List<FlowRuleEntity>> { @Autowired private NacosPropertiesConfiguration nacosPropertiesConfiguration; @Autowired private ConfigService configService; @Autowired private Converter, String> converter; @Override public void publish(String app, List 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)); }}
10. 修改FlowControllerV2类,将上面配置的两个类注入进来,表示规则的拉取和规则的发布统一用我们前面定义的两个实例,然后将FlowControllerV2这个类中的代码覆盖FlowControllerV1的代码
@RestController@RequestMapping(value = "/v2/flow")public class FlowControllerV2 { private final Logger logger = LoggerFactory.getLogger(FlowControllerV2.class); @Autowired private InMemoryRuleRepositoryAdapter repository; @Autowired @Qualifier("flowRuleNacosProvider") private DynamicRuleProvider> ruleProvider; @Autowired @Qualifier("flowRuleNacosPublisher") private DynamicRulePublisher> rulePublisher;
11. 在application.properties文件中添加Nacos服务端的配置信息
sentinel.nacos.serverAddr=192.168.56.1:8848sentinel.nacos.namespacec=sentinel.nacos.group-id=DEFAULT_GROUP
12. 使用下面命令将代码打包成一个 fat jar,然后启动。
mvn clean package
Sentinel Dashboard规则数据同步
对于应用程序来说,需要改动的地方比较少,只需要注意配置文件中的data-id的命名要以-sentinel-flow结尾即可,因为在sentinel dashboard中我们写了一个固定的后缀。
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
1. 登陆Sentinel Dashboard,进入“流控规则”,然后针对指定的资源创建流控规则。
2. 进入Nacos控制台,就可以看到在Sentinel Dashboard中配置的流控规则。