gateway集成sentinel配置nacos持久化GatewayFlowRule规则后–GatewayFlowRule规则失效(规则的时间单位和时间粒度失效)

原因分析

sentinel-dashboard改造的时候模仿的是官方demo中进行改造的

sentinel源码中提供的sentinel-dashboard下test的改造dmeo, 我会在添加相应的注释进行说明

@Configuration

public class NacosConfig {

/**

* 将FlowRuleEntity对象转换为JSON字符串

*/

@Bean

public Converter, String> flowRuleEntityEncoder() {

return JSON::toJSONString;

}

/**

* 将JSON字符串转换为FlowRuleEntity对象

*/

@Bean

public Converter> 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, String> apiDefinitionEntityEncoder() {

return JSON::toJSONString;

}

@Bean

public Converter> apiDefinitionEntityDecoder() {

return s -> JSON.parseArray(s, ApiDefinitionEntity.class);

}

/**

* 网关flowRule

*

* @return

* @throws Exception

*/

@Bean

public Converter> gatewayFlowRuleEntityDecoder() {

return s -> JSON.parseArray(s, GatewayFlowRuleEntity.class);

}

@Bean

public Converter, String> gatewayFlowRuleEntityEncoder() {

return JSON::toJSONString;

}

}

问题就出在sentinel和nacos交互时候List和JSON直接发生了转换, 而实际上sentinel使用的是List

原流程

查询(queryFlowRules)

Sentinel-Nacos的GatewayFlowRule规则的[增删改查]交互的原流程如下

改造前–dashboard从nacos获取限流规则流程 UML时序图源码

如果对使用代码编写这种时序图感兴趣的, 到时候可以考虑单独出一期教程

新增(addFlowRule)

改造前–dashboard同步nacos中新增限流规则 UML时序图源码

修改(updateFlowRule)

改造前–dashboard同步nacos中修改限流规则 UML时序图源码

删除(deleteFlowRule)

改造前–dashboard同步nacos中删除限流规则 UML时序图源码

解决办法

将原来的List<->JSON流程中修改成以下

List <—> List <—> JSON

存储到sentinel中就是List的JSON而不是原来的List, 此时限流规则就可以生效了

步骤一: 修改NacosConfig

@Slf4j

@Configuration

public class NacosConfig {

// 其他代码省略....

// -------------- 改动 start --------------

// 注释掉下述代码, 将修改转换规则, 应该将GatewayFlowRuleEntity转换成GatewayFlowRule再转换成JSON

/*@Bean

public Converter, String> gatewayFlowRuleEntityEncoder() {

return JSON::toJSONString;

}*/

/**

* 网关flowRule

*

* @return

* @throws Exception

*/

@Bean

public Converter, String> gatewayFlowRuleEntityEncoder() {

return entityList -> {

List 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

* @throws Exception

*/

public List 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 ruleList = JSON.parseArray(jsonStr, GatewayFlowRule.class);

// 将GatewayFlowRule列表转换成GatewayFlowRuleEntity列表

List entityList = ruleList.stream()

.map(rule -> GatewayFlowRuleEntity.fromGatewayFlowRule(app, ip, port, rule))

.collect(Collectors.toList());

log.info("JSON字符串:{}, " +

"JSON->List:{}, " +

"List->List:{},",

jsonStr, JSONObject.toJSONString(ruleList), JSONObject.toJSONString(entityList));

return entityList;

}

}

步骤三: 修改GatewayFlowRuleController

// -------------- 改动 start --------------

// @Autowired

// @Qualifier("gatewayFlowRulesNacosProvider")

// private DynamicRuleProvider> 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, 也就说我们JSON必须是List, 到此就恍然大悟了

好文链接

评论可见,请评论后查看内容,谢谢!!!
 您阅读本篇文章共花了: