L'architecture par défaut de Sentinel Dashboard pousse les règles de contrôle de flux directement vers les clients Sentinel via des appels HTTP. Pour centraliser et persister ces conifgurations, il est nécessaire de modifier le code source du Dashboard afin de rediriger ces poussées vers une source de données dynamique telle que Nacos.
Environnement et Dépendances
Cette intégration repose sur les versions suivantes :
- Spring Boot : 2.2.2.RELEASE
- Sentinel Dashboard : 1.7.1
- Nacos Server : 1.2.0
Configuration du Client Spring Boot
Ajoutez les dépendances Maven nécessaires pour activer Sentinel et son connecteur Nacos :
<dependencies>
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
<version>2.2.0.RELEASE</version>
</dependency>
<dependency>
<groupId>com.alibaba.csp</groupId>
<artifactId>sentinel-datasource-nacos</artifactId>
<version>1.7.1</version>
</dependency>
</dependencies>
Configurez ensuite votre fichier application.yml pour définir les points de terminaison Nacos pour les règles de flux et de paramètres chauds :
spring:
application:
name: my-sentinel-client
cloud:
sentinel:
transport:
dashboard: 127.0.0.1:8080
datasource:
flow-rules:
nacos:
server-addr: 127.0.0.1:8848
namespace: development
group-id: SENTINEL_RULES_GROUP
data-id: ${spring.application.name}-flow
rule-type: flow
param-rules:
nacos:
server-addr: 127.0.0.1:8848
namespace: development
group-id: SENTINEL_RULES_GROUP
data-id: ${spring.application.name}-param
rule-type: param-flow
eager: true
web-content-unify: false
Modification du Code Source de Sentinel Dashboard
Le mécanisme natif utilise des fournisseurs (Providers) et des éditeurs (Publishers) basés sur HTTP. Nous devons implémenter des variantes spécifiques à Nacos, notamment pour les règles de paramètres chauds (ParamFlowRule) qui ne sont pas entièrement supportées par défaut.
Implémentation du Fournisseur Nacos
Créez un composant pour récupérer les règles depuis Nacos :
@Component("customParamFlowNacosProvider")
public class CustomParamFlowNacosProvider implements DynamicRuleProvider<List<ParamFlowRuleEntity>> {
@Autowired
private ConfigService nacosConfigService;
@Autowired
private Converter<String, List<ParamFlowRuleEntity>> paramFlowJsonDecoder;
@Override
public List<ParamFlowRuleEntity> getRules(String applicationName) throws Exception {
String configData = nacosConfigService.getConfig(
applicationName + NacosConfigUtil.PARAM_FLOW_DATA_ID_POSTFIX,
NacosConfigUtil.GROUP_ID,
5000
);
if (configData == null || configData.trim().isEmpty()) {
return Collections.emptyList();
}
return paramFlowJsonDecoder.convert(configData);
}
}
Implémentation de l'Éditeur Nacos
Créez le composant correspondant pour publier les modifications vers Nacos :
@Component("customParamFlowNacosPublisher")
public class CustomParamFlowNacosPublisher implements DynamicRulePublisher<List<ParamFlowRuleEntity>> {
@Autowired
private ConfigService nacosConfigService;
@Autowired
private Converter<List<ParamFlowRuleEntity>, String> paramFlowJsonEncoder;
@Override
public void publish(String applicationName, List<ParamFlowRuleEntity> ruleList) throws Exception {
if (applicationName == null || applicationName.trim().isEmpty()) {
throw new IllegalArgumentException("Le nom de l'application ne doit pas être vide");
}
if (ruleList == null) {
return;
}
String payload = paramFlowJsonEncoder.convert(ruleList);
nacosConfigService.publishConfig(
applicationName + NacosConfigUtil.PARAM_FLOW_DATA_ID_POSTFIX,
NacosConfigUtil.GROUP_ID,
payload
);
}
}
Configuration des Beans Nacos
Définissez les convertisseurs JSON et le service de configuration Nacos dans une classe de configuration dédiée :
@Configuration
public class CustomNacosConfiguration {
@Bean
public Converter<List<FlowRuleEntity>, String> flowRuleJsonEncoder() {
return JSON::toJSONString;
}
@Bean
public Converter<String, List<FlowRuleEntity>> flowRuleJsonDecoder() {
return json -> JSON.parseArray(json, FlowRuleEntity.class);
}
@Bean
public Converter<List<ParamFlowRuleEntity>, String> paramFlowRuleJsonEncoder() {
return JSON::toJSONString;
}
@Bean
public Converter<String, List<ParamFlowRuleEntity>> paramFlowRuleJsonDecoder() {
return json -> JSON.parseArray(json, ParamFlowRuleEntity.class);
}
@Bean
public ConfigService nacosConfigService() throws NacosException {
Properties nacosProps = new Properties();
nacosProps.setProperty(PropertyKeyConst.SERVER_ADDR, "127.0.0.1");
nacosProps.setProperty(PropertyKeyConst.NAMESPACE, "development");
return ConfigFactory.createConfigService(nacosProps);
}
}
Contrôleur REST pour les Règles de Paramètres Chauds
Exposez les points de terminaison API pour gérer le cycle de vie des règles. Ce contrôleur remplace l'implémentation par défaut en injectant les composants Nacos personnalisés :
@RestController
@RequestMapping(value = "/api/v2/param-flow")
public class ParamFlowRuleV2Controller {
private static final Logger log = LoggerFactory.getLogger(ParamFlowRuleV2Controller.class);
@Autowired
@Qualifier("customParamFlowNacosProvider")
private DynamicRuleProvider<List<ParamFlowRuleEntity>> dynamicProvider;
@Autowired
@Qualifier("customParamFlowNacosPublisher")
private DynamicRulePublisher<List<ParamFlowRuleEntity>> dynamicPublisher;
@Autowired
private AppManagement appManagement;
@Autowired
private RuleRepository<ParamFlowRuleEntity, Long> repository;
@GetMapping("/rules")
@AuthAction(PrivilegeType.READ_RULE)
public Result<List<ParamFlowRuleEntity>> fetchRules(@RequestParam String app,
@RequestParam String ip,
@RequestParam Integer port) {
if (StringUtil.isEmpty(app) || StringUtil.isEmpty(ip) || port == null || port <= 0) {
return Result.ofFail(-1, "Paramètres invalides");
}
try {
List<ParamFlowRuleEntity> rules = dynamicProvider.getRules(app);
if (rules != null && !rules.isEmpty()) {
rules.forEach(r -> {
r.setApp(app);
if (r.getClusterConfig() != null && r.getClusterConfig().getFlowId() != null) {
r.setId(r.getClusterConfig().getFlowId());
}
});
}
repository.saveAll(rules);
return Result.ofSuccess(rules);
} catch (Exception ex) {
log.error("Échec de la récupération des règles", ex);
return Result.ofFail(-1, ex.getMessage());
}
}
@PostMapping("/rule")
@AuthAction(PrivilegeType.WRITE_RULE)
public Result<ParamFlowRuleEntity> createRule(@RequestBody ParamFlowRuleEntity entity) {
entity.setId(null);
entity.setGmtCreate(new Date());
entity.setGmtModified(new Date());
try {
ParamFlowRuleEntity saved = repository.save(entity);
syncRulesToNacos(entity.getApp());
return Result.ofSuccess(saved);
} catch (Exception ex) {
log.error("Échec de la création de la règle", ex);
return Result.ofFail(-1, ex.getMessage());
}
}
private void syncRulesToNacos(String app) throws Exception {
List<ParamFlowRuleEntity> currentRules = repository.findAllByApp(app);
dynamicPublisher.publish(app, currentRules);
}
}
Adaptation de l'Interface Frontend
Pour que le tableau de bord utilise ce nouveau contrôleur, vous devez modifier le fichier JavaScript compilé. Localisez resources/dist/js/app.js et remplacez les appels à /paramFlow/rules par /api/v2/param-flow/rules.
Ajustement de l'Entité de Règle
Enfin, assurez-vous que l'entité ParamFlowRuleEntity sérialise correctement les propriétés nécessaires pour Nacos en ajustant les annotations JSON :
public class ParamFlowRuleEntity extends AbstractRuleEntity<ParamFlowRule> {
public ParamFlowRuleEntity(ParamFlowRule rule) {
AssertUtil.notNull(rule, "La règle ne doit pas être nulle");
this.rule = rule;
}
public String getResource() {
return rule.getResource();
}
public double getCount() {
return rule.getCount();
}
@JsonIgnore
@JSONField(serialize = false)
public List<ParamFlowItem> getParamFlowItemList() {
return rule.getParamFlowItemList();
}
public int getControlBehavior() {
return rule.getControlBehavior();
}
public long getDurationInSec() {
return rule.getDurationInSec();
}
}