在网上找了很多的资料,发现sentinel整合nacos持久化的博文和视频大多数都只有改造限流部分的教程,并且都需要修改前端,略显麻烦,至于剩下的熔断、热点流控、授权的更是没有相关的改造教程,最后在知乎的看到一篇文章后让我大受启发
这位前辈讲到sentinel原来是把配置保存到内存的,我们只需要找出这行保存到内存的代码,把它改为发布到nacos就可以了,这样做改动非常的小,而且不用改前端,是个非常聪明的思路。
下面是我改造的过程,完全不需要改前端:
注释掉nacos依赖的test作用域
把test目录下nacos文件夹复制一份到java目录下
改造NacosConfig 类
/*
* Copyright 1999-2018 Alibaba Group Holding Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.csp.sentinel.dashboard.rule.nacos;
import java.util.List;
import java.util.Properties;
import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.AuthorityRuleEntity;
import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.DegradeRuleEntity;
import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.FlowRuleEntity;
import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.ParamFlowRuleEntity;
import com.alibaba.csp.sentinel.datasource.Converter;
import com.alibaba.fastjson.JSON;
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;
/**
* @author Eric Zhao
* @since 1.4.0
*/
@Configuration
public class NacosConfig {
// 动态配置nacos地址,用户名,密码
@Value("${sentinel.nacos.serverAddr}")
private String serverAddr;
@Value("${sentinel.nacos.username}")
private String username;
@Value("${sentinel.nacos.password}")
private String password;
/**
* 加入要改造方法所需实体的编解码器
* @return
*/
// 流控
@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 Converter<List<DegradeRuleEntity>, String> degradeRuleEntityEncoder() {
return JSON::toJSONString;
}
@Bean
public Converter<String, List<DegradeRuleEntity>> degradeRuleEntityDecoder() {
return s -> JSON.parseArray(s, DegradeRuleEntity.class);
}
// 授权
@Bean
public Converter<List<AuthorityRuleEntity>, String> authorityRuleEntityEncoder() {
return JSON::toJSONString;
}
@Bean
public Converter<String, List<AuthorityRuleEntity>> authorityRuleEntityDecoder() {
return s -> JSON.parseArray(s, AuthorityRuleEntity.class);
}
// 热点流控
@Bean
public Converter<List<ParamFlowRuleEntity>, String> paramFlowRuleEntityEncoder() {
return JSON::toJSONString;
}
@Bean
public Converter<String, List<ParamFlowRuleEntity>> paramFlowRuleEntityDecoder() {
return s -> JSON.parseArray(s, ParamFlowRuleEntity.class);
}
@Bean
public ConfigService nacosConfigService() throws Exception {
Properties properties = new Properties();
properties.put("serverAddr", serverAddr);
properties.put("username", username);
properties.put("password", password);
return ConfigFactory.createConfigService(properties);
// return ConfigFactory.createConfigService("localhost");
}
}
定义发布到配置中心的配置名后缀
/*
* Copyright 1999-2018 Alibaba Group Holding Ltd.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.alibaba.csp.sentinel.dashboard.rule.nacos;
/**
* @author Eric Zhao
* @since 1.4.0
*/
public final class NacosConfigUtil {
public static final String GROUP_ID = "SENTINEL_GROUP";
// 定义发布到配置中心的配置名后缀
public static final String FLOW_DATA_ID_POSTFIX = "-flow-rules";
public static final String DEGRADE_DATA_ID_POSTFIX = "-degrade-rules";
public static final String PARAM_FLOW_DATA_ID_POSTFIX = "-param-rules";
public static final String AUTHORITY_DATA_ID_POSTFIX = "-authority-rules";
public static final String CLUSTER_MAP_DATA_ID_POSTFIX = "-cluster-map";
/**
* cc for `cluster-client`
*/
public static final String CLIENT_CONFIG_DATA_ID_POSTFIX = "-cc-config";
/**
* cs for `cluster-server`
*/
public static final String SERVER_TRANSPORT_CONFIG_DATA_ID_POSTFIX = "-cs-transport-config";
public static final String SERVER_FLOW_CONFIG_DATA_ID_POSTFIX = "-cs-flow-config";
public static final String SERVER_NAMESPACE_SET_DATA_ID_POSTFIX = "-cs-namespace-set";
private NacosConfigUtil() {}
}
配置文件中加入需要自定义的配置
#spring settings
server.servlet.encoding.force=true
server.servlet.encoding.charset=UTF-8
server.servlet.encoding.enabled=true
#cookie name setting
server.servlet.session.cookie.name=sentinel_dashboard_cookie
#logging settings
logging.level.org.springframework.web=INFO
logging.file.name=${user.home}/logs/csp/sentinel-dashboard.log
logging.pattern.file= %d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n
#logging.pattern.console= %d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n
#auth settings
auth.filter.exclude-urls=/,/auth/login,/auth/logout,/registry/machine,/version
auth.filter.exclude-url-suffixes=htm,html,js,css,map,ico,ttf,woff,png
# If auth.enabled=false, Sentinel console disable login
auth.username=sentinel
auth.password=sentinel
# Inject the dashboard version. It's required to enable
# filtering in pom.xml for this resource file.
sentinel.dashboard.version=@project.version@
sentinel.nacos.serverAddr = 127.0.0.1:8848
sentinel.nacos.username = nacos
sentinel.nacos.password = nacos
sentinel.nacos.groupId = SENTINEL_GROUP
# custom dataId postfix
sentinel.custom.FLOW_DATA_ID_POSTFIX =
sentinel.custom.DEGRADE_DATA_ID_POSTFIX =
sentinel.custom.AUTHORITY_DATA_ID_POSTFIX =
sentinel.custom.PARAM_FLOW_DATA_ID_POSTFIX =
FlowRuleNacosProvider 类改造
package com.alibaba.csp.sentinel.dashboard.rule.nacos;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.FlowRuleEntity;
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.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
/**
* @author Eric Zhao
* @since 1.4.0
*/
@Component("flowRuleNacosProvider")
public class FlowRuleNacosProvider implements DynamicRuleProvider<List<FlowRuleEntity>> {
@Autowired
private ConfigService configService;
@Autowired
private Converter<String, List<FlowRuleEntity>> converter;
// 增加的代码
@Value("${sentinel.nacos.groupId}")
private String groupId;
// 增加的代码
@Value("${sentinel.custom.FLOW_DATA_ID_POSTFIX}")
private String dataIdPostfix;
@Override
public List<FlowRuleEntity> getRules(String appName) throws Exception {
// 增加的代码 用于判断是否自定义dataid后缀
if (dataIdPostfix.isEmpty()){
dataIdPostfix = NacosConfigUtil.FLOW_DATA_ID_POSTFIX;
}
String rules = configService.getConfig(appName + dataIdPostfix,
groupId, 3000);
if (StringUtil.isEmpty(rules)) {
return new ArrayList<>();
}
return converter.convert(rules);
}
}
FlowRuleNacosPublisher 类的改造
package com.alibaba.csp.sentinel.dashboard.rule.nacos;
import java.util.List;
import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.FlowRuleEntity;
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.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
/**
* @author Eric Zhao
* @since 1.4.0
*/
@Component("flowRuleNacosPublisher")
public class FlowRuleNacosPublisher implements DynamicRulePublisher<List<FlowRuleEntity>> {
@Autowired
private ConfigService configService;
@Autowired
private Converter<List<FlowRuleEntity>, String> converter;
// 增加的代码
@Value("${sentinel.nacos.groupId}")
private String groupId;
// 增加的代码
@Value("${sentinel.custom.FLOW_DATA_ID_POSTFIX}")
private String dataIdPostfix;
@Override
public void publish(String app, List<FlowRuleEntity> rules) throws Exception {
// 增加的代码 用于判断是否自定义dataid后缀
if (dataIdPostfix.isEmpty()){
dataIdPostfix = NacosConfigUtil.FLOW_DATA_ID_POSTFIX;
}
AssertUtil.notEmpty(app, "app name cannot be empty");
if (rules == null) {
return;
}
// configService.publishConfig(app + NacosConfigUtil.FLOW_DATA_ID_POSTFIX,
// NacosConfigUtil.GROUP_ID, converter.convert(rules));
configService.publishConfig(app + dataIdPostfix,
groupId, converter.convert(rules));
}
}
熔断、授权、热点流控Provider和Publisher的改造与流控的类似,改下对应实体类型、后缀、组件名即可
FlowControllerV1 类的改造
package com.alibaba.csp.sentinel.dashboard.controller;
import java.util.Date;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import com.alibaba.csp.sentinel.dashboard.auth.AuthAction;
import com.alibaba.csp.sentinel.dashboard.auth.AuthService.PrivilegeType;
import com.alibaba.csp.sentinel.dashboard.discovery.AppManagement;
import com.alibaba.csp.sentinel.dashboard.rule.DynamicRuleProvider;
import com.alibaba.csp.sentinel.dashboard.rule.DynamicRulePublisher;
import com.alibaba.csp.sentinel.util.StringUtil;
import com.alibaba.csp.sentinel.dashboard.client.SentinelApiClient;
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.repository.rule.InMemoryRuleRepositoryAdapter;
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.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
/**
* Flow rule controller.
*
* @author leyou
* @author Eric Zhao
*/
@RestController
@RequestMapping(value = "/v1/flow")
public class FlowControllerV1 {
private final Logger logger = LoggerFactory.getLogger(FlowControllerV1.class);
@Autowired
private InMemoryRuleRepositoryAdapter<FlowRuleEntity> repository;
@Autowired
private AppManagement appManagement;
@Autowired
private SentinelApiClient sentinelApiClient;
// 增加的代码,注入用于发布nacos配置的类
@Autowired
@Qualifier("flowRuleNacosProvider")
private DynamicRuleProvider<List<FlowRuleEntity>> ruleProvider;
@Autowired
@Qualifier("flowRuleNacosPublisher")
private DynamicRulePublisher<List<FlowRuleEntity>> rulePublisher;
@GetMapping("/rules")
@AuthAction(PrivilegeType.READ_RULE)
public Result<List<FlowRuleEntity>> apiQueryMachineRules(@RequestParam String app,
@RequestParam String ip,
@RequestParam 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");
}
if (!appManagement.isValidMachineOfApp(app, ip)) {
return Result.ofFail(-1, "given ip does not belong to given app");
}
try {
// List<FlowRuleEntity> rules = sentinelApiClient.fetchFlowRuleOfMachine(app, ip, port);
// 增加的代码,把查询内存改为查询naocos配置中心
List<FlowRuleEntity> rules = ruleProvider.getRules(app);
if (rules != null && !rules.isEmpty()) {
for (FlowRuleEntity entity : rules) {
entity.setApp(app);
if (entity.getClusterConfig() != null && entity.getClusterConfig().getFlowId() != null) {
entity.setId(entity.getClusterConfig().getFlowId());
}
}
}
rules = repository.saveAll(rules);
return Result.ofSuccess(rules);
} catch (Throwable throwable) {
logger.error("Error when querying flow rules", throwable);
return Result.ofThrowable(-1, throwable);
}
}
...
private CompletableFuture<Void> publishRules(String app, String ip, Integer port) throws Exception {
List<FlowRuleEntity> rules = repository.findAllByMachine(MachineInfo.of(app, ip, port));
// 增加的代码,把新增的规则发布到指定的nacos配置中心
rulePublisher.publish(app, rules);
// 保留保存到内存的逻辑
return sentinelApiClient.setFlowRuleOfMachineAsync(app, ip, port, rules);
}
}
DegradeController 类改造
package com.alibaba.csp.sentinel.dashboard.controller;
import com.alibaba.csp.sentinel.dashboard.auth.AuthAction;
import com.alibaba.csp.sentinel.dashboard.auth.AuthService.PrivilegeType;
import com.alibaba.csp.sentinel.dashboard.client.SentinelApiClient;
import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.DegradeRuleEntity;
import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.FlowRuleEntity;
import com.alibaba.csp.sentinel.dashboard.discovery.AppManagement;
import com.alibaba.csp.sentinel.dashboard.discovery.MachineInfo;
import com.alibaba.csp.sentinel.dashboard.domain.Result;
import com.alibaba.csp.sentinel.dashboard.repository.rule.RuleRepository;
import com.alibaba.csp.sentinel.dashboard.rule.DynamicRuleProvider;
import com.alibaba.csp.sentinel.dashboard.rule.DynamicRulePublisher;
import com.alibaba.csp.sentinel.slots.block.RuleConstant;
import com.alibaba.csp.sentinel.slots.block.degrade.circuitbreaker.CircuitBreakerStrategy;
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.Date;
import java.util.List;
/**
* Controller regarding APIs of degrade rules. Refactored since 1.8.0.
*
* @author Carpenter Lee
* @author Eric Zhao
*/
@RestController
@RequestMapping("/degrade")
public class DegradeController {
private final Logger logger = LoggerFactory.getLogger(DegradeController.class);
@Autowired
private RuleRepository<DegradeRuleEntity, Long> repository;
@Autowired
private SentinelApiClient sentinelApiClient;
// 增加的代码,注入用于发布nacos配置的类
@Autowired
@Qualifier("degradeRuleNacosProvider")
private DynamicRuleProvider<List<DegradeRuleEntity>> ruleProvider;
@Autowired
@Qualifier("degradeRuleNacosPublisher")
private DynamicRulePublisher<List<DegradeRuleEntity>> rulePublisher;
@Autowired
private AppManagement appManagement;
@GetMapping("/rules.json")
@AuthAction(PrivilegeType.READ_RULE)
public Result<List<DegradeRuleEntity>> apiQueryMachineRules(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");
}
if (!appManagement.isValidMachineOfApp(app, ip)) {
return Result.ofFail(-1, "given ip does not belong to given app");
}
try {
// 增加的代码 查询nacos配置中心
List<DegradeRuleEntity> rules = ruleProvider.getRules(app);
if (rules != null && !rules.isEmpty()) {
for (DegradeRuleEntity entity : rules) {
entity.setApp(app);
}
}
// List<DegradeRuleEntity> rules = sentinelApiClient.fetchDegradeRuleOfMachine(app, ip, port);
rules = repository.saveAll(rules);
return Result.ofSuccess(rules);
} catch (Throwable throwable) {
logger.error("queryApps error:", throwable);
return Result.ofThrowable(-1, throwable);
}
}
....
private boolean publishRules(String app, String ip, Integer port) throws Exception {
List<DegradeRuleEntity> rules = repository.findAllByMachine(MachineInfo.of(app, ip, port));
// 增加的代码 保存到nacos
rulePublisher.publish(app, rules);
return sentinelApiClient.setDegradeRuleOfMachine(app, ip, port, rules);
}
}
AuthorityRuleController 类的改造
import java.util.Date;
import java.util.List;
import com.alibaba.csp.sentinel.dashboard.auth.AuthAction;
import com.alibaba.csp.sentinel.dashboard.client.SentinelApiClient;
import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.FlowRuleEntity;
import com.alibaba.csp.sentinel.dashboard.discovery.AppManagement;
import com.alibaba.csp.sentinel.dashboard.discovery.MachineInfo;
import com.alibaba.csp.sentinel.dashboard.auth.AuthService.PrivilegeType;
import com.alibaba.csp.sentinel.dashboard.rule.DynamicRuleProvider;
import com.alibaba.csp.sentinel.dashboard.rule.DynamicRulePublisher;
import com.alibaba.csp.sentinel.slots.block.RuleConstant;
import com.alibaba.csp.sentinel.util.StringUtil;
import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.AuthorityRuleEntity;
import com.alibaba.csp.sentinel.dashboard.domain.Result;
import com.alibaba.csp.sentinel.dashboard.repository.rule.RuleRepository;
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.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
/**
* @author Eric Zhao
* @since 0.2.1
*/
@RestController
@RequestMapping(value = "/authority")
public class AuthorityRuleController {
private final Logger logger = LoggerFactory.getLogger(AuthorityRuleController.class);
@Autowired
private SentinelApiClient sentinelApiClient;
@Autowired
private RuleRepository<AuthorityRuleEntity, Long> repository;
@Autowired
private AppManagement appManagement;
// 增加的代码,注入用于发布nacos配置的类
@Autowired
@Qualifier("authorityRuleNacosProvider")
private DynamicRuleProvider<List<AuthorityRuleEntity>> ruleProvider;
@Autowired
@Qualifier("authorityRuleNacosPublisher")
private DynamicRulePublisher<List<AuthorityRuleEntity>> rulePublisher;
@GetMapping("/rules")
@AuthAction(PrivilegeType.READ_RULE)
public Result<List<AuthorityRuleEntity>> apiQueryAllRulesForMachine(@RequestParam String app,
@RequestParam String ip,
@RequestParam Integer port) {
if (StringUtil.isEmpty(app)) {
return Result.ofFail(-1, "app cannot be null or empty");
}
if (StringUtil.isEmpty(ip)) {
return Result.ofFail(-1, "ip cannot be null or empty");
}
if (port == null || port <= 0) {
return Result.ofFail(-1, "Invalid parameter: port");
}
if (!appManagement.isValidMachineOfApp(app, ip)) {
return Result.ofFail(-1, "given ip does not belong to given app");
}
try {
// List<AuthorityRuleEntity> rules = sentinelApiClient.fetchAuthorityRulesOfMachine(app, ip, port);
// 增加的代码 查询nacos配置中心
List<AuthorityRuleEntity> rules = ruleProvider.getRules(app);
if (rules != null && !rules.isEmpty()) {
for (AuthorityRuleEntity entity : rules) {
entity.setApp(app);
}
}
rules = repository.saveAll(rules);
return Result.ofSuccess(rules);
} catch (Throwable throwable) {
logger.error("Error when querying authority rules", throwable);
return Result.ofFail(-1, throwable.getMessage());
}
}
...
private boolean publishRules(String app, String ip, Integer port) throws Exception {
List<AuthorityRuleEntity> rules = repository.findAllByMachine(MachineInfo.of(app, ip, port));
// 增加的代码 保存到nacos
rulePublisher.publish(app, rules);
return sentinelApiClient.setAuthorityRuleOfMachine(app, ip, port, rules);
}
}
ParamFlowRuleController 类的改造,Provider查询类返回类型不同,对应的Provider类也要做相应的修改
package com.alibaba.csp.sentinel.dashboard.controller;
import java.util.Date;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.stream.Collectors;
import com.alibaba.csp.sentinel.dashboard.auth.AuthAction;
import com.alibaba.csp.sentinel.dashboard.client.CommandNotFoundException;
import com.alibaba.csp.sentinel.dashboard.client.SentinelApiClient;
import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.FlowRuleEntity;
import com.alibaba.csp.sentinel.dashboard.discovery.AppManagement;
import com.alibaba.csp.sentinel.dashboard.discovery.MachineInfo;
import com.alibaba.csp.sentinel.dashboard.auth.AuthService;
import com.alibaba.csp.sentinel.dashboard.auth.AuthService.PrivilegeType;
import com.alibaba.csp.sentinel.dashboard.rule.DynamicRuleProvider;
import com.alibaba.csp.sentinel.dashboard.rule.DynamicRulePublisher;
import com.alibaba.csp.sentinel.dashboard.rule.nacos.ParamFlowRuleNacosProvider;
import com.alibaba.csp.sentinel.slots.block.RuleConstant;
import com.alibaba.csp.sentinel.util.StringUtil;
import com.alibaba.csp.sentinel.dashboard.datasource.entity.SentinelVersion;
import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.ParamFlowRuleEntity;
import com.alibaba.csp.sentinel.dashboard.domain.Result;
import com.alibaba.csp.sentinel.dashboard.repository.rule.RuleRepository;
import com.alibaba.csp.sentinel.dashboard.util.VersionUtils;
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.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
/**
* @author Eric Zhao
* @since 0.2.1
*/
@RestController
@RequestMapping(value = "/paramFlow")
public class ParamFlowRuleController {
private final Logger logger = LoggerFactory.getLogger(ParamFlowRuleController.class);
@Autowired
private SentinelApiClient sentinelApiClient;
@Autowired
private AppManagement appManagement;
@Autowired
private RuleRepository<ParamFlowRuleEntity, Long> repository;
// 增加的代码,注入用于发布nacos配置的类
// 这个查询返回的类型与其他三个方法有些区别
@Autowired
@Qualifier("paramFlowRuleNacosProvider")
private ParamFlowRuleNacosProvider ruleProvider;
@Autowired
@Qualifier("paramFlowRuleNacosPublisher")
private DynamicRulePublisher<List<ParamFlowRuleEntity>> rulePublisher;
private boolean checkIfSupported(String app, String ip, int port) {
try {
return Optional.ofNullable(appManagement.getDetailApp(app))
.flatMap(e -> e.getMachine(ip, port))
.flatMap(m -> VersionUtils.parseVersion(m.getVersion())
.map(v -> v.greaterOrEqual(version020)))
.orElse(true);
// If error occurred or cannot retrieve machine info, return true.
} catch (Exception ex) {
return true;
}
}
@GetMapping("/rules")
@AuthAction(PrivilegeType.READ_RULE)
public Result<List<ParamFlowRuleEntity>> apiQueryAllRulesForMachine(@RequestParam String app,
@RequestParam String ip,
@RequestParam Integer port) {
if (StringUtil.isEmpty(app)) {
return Result.ofFail(-1, "app cannot be null or empty");
}
if (StringUtil.isEmpty(ip)) {
return Result.ofFail(-1, "ip cannot be null or empty");
}
if (port == null || port <= 0) {
return Result.ofFail(-1, "Invalid parameter: port");
}
if (!appManagement.isValidMachineOfApp(app, ip)) {
return Result.ofFail(-1, "given ip does not belong to given app");
}
if (!checkIfSupported(app, ip, port)) {
return unsupportedVersion();
}
try {
// 增加的代码 查询nacos配置中心 与其他三个方法有些区别
return ruleProvider.getRules(app)
.thenApply(repository::saveAll)
.thenApply(Result::ofSuccess)
.get();
// return sentinelApiClient.fetchParamFlowRulesOfMachine(app, ip, port)
// .thenApply(repository::saveAll)
// .thenApply(Result::ofSuccess)
// .get();
} catch (ExecutionException ex) {
logger.error("Error when querying parameter flow rules", ex.getCause());
if (isNotSupported(ex.getCause())) {
return unsupportedVersion();
} else {
return Result.ofThrowable(-1, ex.getCause());
}
} catch (Throwable throwable) {
logger.error("Error when querying parameter flow rules", throwable);
return Result.ofFail(-1, throwable.getMessage());
}
}
private boolean isNotSupported(Throwable ex) {
return ex instanceof CommandNotFoundException;
}
...
private CompletableFuture<Void> publishRules(String app, String ip, Integer port) throws Exception {
List<ParamFlowRuleEntity> rules = repository.findAllByMachine(MachineInfo.of(app, ip, port));
// 增加的代码 保存到nacos
rulePublisher.publish(app, rules);
return sentinelApiClient.setParamFlowRuleOfMachine(app, ip, port, rules);
}
}
具体的ParamFlowRuleNacosProvider 类
package com.alibaba.csp.sentinel.dashboard.rule.nacos;
import com.alibaba.csp.sentinel.dashboard.datasource.entity.rule.ParamFlowRuleEntity;
import com.alibaba.csp.sentinel.datasource.Converter;
import com.alibaba.fastjson.JSON;
import com.alibaba.nacos.api.config.ConfigService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.concurrent.CompletableFuture;
/**
* @ClassName: ParamFlowRuleNacosProvider
* @Description: TODO
* @Author: ChiKin Lau @ SUSE
* @Date: 2023/11/8 10:08
* @version: 1.0
**/
@Component("paramFlowRuleNacosProvider")
public class ParamFlowRuleNacosProvider {
@Autowired
private ConfigService configService;
@Autowired
private Converter<String, List<ParamFlowRuleEntity>> converter;
// 增加的代码
@Value("${sentinel.nacos.groupId}")
private String groupId;
@Value("${sentinel.custom.PARAM_FLOW_DATA_ID_POSTFIX}")
private String dataIdPostfix;
public CompletableFuture<List<ParamFlowRuleEntity>> getRules(String appName) throws Exception {
// 增加的代码 用于判断是否自定义dataid后缀
if (dataIdPostfix.isEmpty()){
dataIdPostfix = NacosConfigUtil.PARAM_FLOW_DATA_ID_POSTFIX;
}
String rules = configService.getConfig(appName + dataIdPostfix,
groupId, 3000);
// if (StringUtil.isEmpty(rules)) {
// return new CompletableFuture<>();
// }
// 增加的代码 用于适应不同的返回类型
CompletableFuture<String> future = new CompletableFuture<>();
future.complete(rules);
return future.thenApply(json -> JSON.parseArray(json, ParamFlowRuleEntity.class));
}
}
OK,至此所有的改造完成了,使用maven进行打包即可使用,使用这个jar包在页面端的sentinel配可以保存到nacos进行持久化了,包括了流控、熔断、授权和热点限流。
下面附上
源码的GitHub地址: https://github.com/chikin-lau/sentinel-nacos
jar包及使用说明压缩包:
链接:https://pan.baidu.com/s/1jvcSCQw-YjeT5-nRocQOUA
提取码:8xry
–来自百度网盘超级会员V3的分享
参考文章:
https://zhuanlan.zhihu.com/p/663149010