gateway集成sentinel配置nacos持久化GatewayFlowRule规则后--GatewayFlowRule规则失效(规则的时间单位和时间粒度失效)
gateway集成sentinel配置nacos持久化GatewayFlowRule规则后–GatewayFlowRule规则失效(规则的时间单位和时间粒度失效)
原因分析
sentinel-dashboard改造的时候模仿的是官方demo中进行改造的
sentinel源码中提供的sentinel-dashboard下test的改造dmeo, 我会在添加相应的注释进行说明
@Configuration
public class NacosConfig {
/**
* 将FlowRuleEntity对象转换为JSON字符串
*/
@Bean
public Converter<List<FlowRuleEntity>, String> flowRuleEntityEncoder() {
return JSON::toJSONString;
}
/**
* 将JSON字符串转换为FlowRuleEntity对象
*/
@Bean
public Converter<String, List<FlowRuleEntity>> flowRuleEntityDecoder() {
return s -> JSON.parseArray(s, FlowRuleEntity.class);
}
/**
* nacos配置服务, 这里指向本地nacos
*/
@Bean
public ConfigService nacosConfigService() throws Exception {
return ConfigFactory.createConfigService("localhost");
}
}
按照上述风格改造后的NacosConfig
@Slf4j
@Configuration
public class NacosConfig {
// ---------- nacos相关的配置 start ----------
@Value("${sentinel.nacos.serverAddr}")
private String serverAddr;
@Value("${sentinel.nacos.username}")
private String username;
@Value("${sentinel.nacos.password}")
private String password;
@Value("${sentinel.nacos.namespace}")
private String namespace;
/**
* nacos配置服务
*/
@Bean
public ConfigService nacosConfigService() throws Exception {
Properties properties = new Properties();
properties.put(PropertyKeyConst.SERVER_ADDR, serverAddr);
properties.put(PropertyKeyConst.NAMESPACE, namespace);
properties.put(PropertyKeyConst.USERNAME, username);
properties.put(PropertyKeyConst.PASSWORD, password);
return ConfigFactory.createConfigService(properties);
}
// ---------- nacos相关的配置 end ----------
/**
* 网关API
*
* @return
* @throws Exception
*/
@Bean
public Converter<List<ApiDefinitionEntity>, String> apiDefinitionEntityEncoder() {
return JSON::toJSONString;
}
@Bean
public Converter<String, List<ApiDefinitionEntity>> apiDefinitionEntityDecoder() {
return s -> JSON.parseArray(s, ApiDefinitionEntity.class);
}
/**
* 网关flowRule
*
* @return
* @throws Exception
*/
@Bean
public Converter<String, List<GatewayFlowRuleEntity>> gatewayFlowRuleEntityDecoder() {
return s -> JSON.parseArray(s, GatewayFlowRuleEntity.class);
}
@Bean
public Converter<List<GatewayFlowRuleEntity>, String> gatewayFlowRuleEntityEncoder() {
return JSON::toJSONString;
}
}
问题就出在sentinel和nacos交互时候
List<GatewayFlowRuleEntity>
和JSON
直接发生了转换, 而实际上sentinel使用的是List<GatewayFlowRule>
原流程
查询(queryFlowRules)
Sentinel-Nacos的GatewayFlowRule规则的[增删改查]交互的原流程如下
改造前–dashboard从nacos获取限流规则流程 UML时序图源码
如果对使用代码编写这种时序图感兴趣的, 到时候可以考虑单独出一期教程
新增(addFlowRule)
改造前–dashboard同步nacos中新增限流规则 UML时序图源码
修改(updateFlowRule)
改造前–dashboard同步nacos中修改限流规则 UML时序图源码
删除(deleteFlowRule)
改造前–dashboard同步nacos中删除限流规则 UML时序图源码
解决办法
将原来的
List<GatewayFlowRuleEntity>
<->JSON
流程中修改成以下
List<GatewayFlowRuleEntity>
<—>List<GatewayFlowRule>
<—>JSON
存储到sentinel中就是
List<GatewayFlowRule>
的JSON而不是原来的List<GatewayFlowRuleEntity>
, 此时限流规则就可以生效了
步骤一: 修改NacosConfig
@Slf4j
@Configuration
public class NacosConfig {
// 其他代码省略....
// -------------- 改动 start --------------
// 注释掉下述代码, 将修改转换规则, 应该将GatewayFlowRuleEntity转换成GatewayFlowRule再转换成JSON
/*@Bean
public Converter<List<GatewayFlowRuleEntity>, String> gatewayFlowRuleEntityEncoder() {
return JSON::toJSONString;
}*/
/**
* 网关flowRule
*
* @return
* @throws Exception
*/
@Bean
public Converter<List<GatewayFlowRuleEntity>, String> gatewayFlowRuleEntityEncoder() {
return entityList -> {
List<GatewayFlowRule> ruleList = entityList.stream()
.map(GatewayFlowRuleEntity::toGatewayFlowRule)
.collect(Collectors.toList());
String jsonStr = JSONObject.toJSONString(ruleList);
log.info("转换后的JSON字符串:{}", jsonStr);
return jsonStr;
};
}
// ---------- 规则转换器 end ----------
// -------------- 改动 end --------------
}
步骤二 : 修改GatewayFlowRulesNacosProvider
将原来的GatewayFlowRulesNacosProvider
类替换成
@Slf4j
@Component("gatewayFlowRulesNacosProvider")
public class GatewayFlowRulesNacosProvider {
@Autowired
private ConfigService configService;
/**
* 获取网关流控规则
* @param app application服务名
* @param ip ip地址
* @param port 端口
* @return 转换号的List<GatewayFlowRuleEntity>
* @throws Exception
*/
public List<GatewayFlowRuleEntity> getRules(String app, String ip, Integer port) throws Exception {
String jsonStr = configService.getConfig(
app + NacosConfigUtil.GATEWAY_FLOW_DATA_ID_POSTFIX,
NacosConfigUtil.GROUP_ID,
3000);
if (StrUtil.isEmpty(jsonStr)) {
return new ArrayList<>();
}
// 将获取到的JSON字符串转换成GatewayFlowRule列表
List<GatewayFlowRule> ruleList = JSON.parseArray(jsonStr, GatewayFlowRule.class);
// 将GatewayFlowRule列表转换成GatewayFlowRuleEntity列表
List<GatewayFlowRuleEntity> entityList = ruleList.stream()
.map(rule -> GatewayFlowRuleEntity.fromGatewayFlowRule(app, ip, port, rule))
.collect(Collectors.toList());
log.info("JSON字符串:{}, " +
"JSON->List<GatewayFlowRule>:{}, " +
"List<GatewayFlowRule>->List<GatewayFlowRuleEntity>:{},",
jsonStr, JSONObject.toJSONString(ruleList), JSONObject.toJSONString(entityList));
return entityList;
}
}
步骤三: 修改GatewayFlowRuleController
// -------------- 改动 start --------------
// @Autowired
// @Qualifier("gatewayFlowRulesNacosProvider")
// private DynamicRuleProvider<List<GatewayFlowRuleEntity>> gatewayFlowProvider;
@Autowired
@Qualifier("gatewayFlowRulesNacosProvider")
private GatewayFlowRulesNacosProvider gatewayFlowProvider;
// -------------- 改动 end --------------
现流程
查询(queryFlowRules)
时序图如下
改造后–dashboard从nacos中获取限流规则 UML时序图源码
新增(addFlowRule)
改造后–dashboard同步nacos中新增限流规则 UML时序图源码
修改(updateFlowRule)
改造后–dashboard同步nacos中修改限流规则 UML时序图源码
删除(deleteFlowRule)
改造后–dashboard同步nacos中删除限流规则 UML时序图源码
总结
其实笔者在写这块的时候, 参考了大量的博客, 但是那些博客实现了持久化是有缺陷的, sentinel-dashboard中操作缺失能同步到nacos, 但是这里的规则是有问题的, 无法使用的, 和我们理想中的效果大相径庭, 排查的思路大概如下
- 开始怀疑是sentinel内存中的规则配置的优先级高级nacos中的配置 , 于是打日志查看, 发现一旦配置了nacos持久化规则(此时内存中没有配置), 那么就会出现两条规则(一条nacos中的, 一条内存中的), 但是通过修改时, 发现nacos中发生了变化, 但是内存中却没发生变化, 最终生效的还是内存当中的
- 怀疑是sentinl版本和Spring Boot Alibaba版本或Sping Boot版本不兼容导致的, 后续切换成对应的版本的也是不生效的
- 后来又去若依的官网issue(项目基于若依二开的)上查看, 上边导致失效的原因都不是我想要的
- 实在受不了, 最后打断点查看, 发现基于内存版本的
queryFlowRules
会计算intervalSec
的值, 而这个值又依赖interval
和intervalUnit
, 最后发现有一部操作会将JSON转换成List<GatewayFlowRule>
, 也就说我们JSON必须是List<GatewayFlowRule>
, 到此就恍然大悟了
更多推荐
所有评论(0)