使用Sentinel规则持久化后,新增规则时导致旧规则丢失问题排查

上篇我们说到Sentinel控制台规则双向持久化,这一块是没毛病的,防止应用重启配置丢失。
但是我在测试的时候发现,规则持久化之后,重启控制台新增规则的时候会导致一些之前持久化的数据丢失,究竟发生了什么,我们一起来看下:

首先看看我们初始化拉取规则sentinel做了什么(找到 /list.json API)?

//拉取配置中心规则列表
List<GatewayFlowRuleEntity> rules = ruleProvider.getRules(app);
//保存在内存中
repository.saveAll(rules);

注意清看saveAll方法

    @Override
    public List<T> saveAll(List<T> rules) {
        //清空内存
        allRules.clear();
        machineRules.clear();
        appRules.clear();
        if (rules == null) {
            return null;
        }
        List<T> savedRules = new ArrayList<>(rules.size());
        for (T rule : rules) {
        	//遍历放入内存
            savedRules.add(save(rule));
        }
        return savedRules;
    }
    @Override
    public T save(T entity) {
        if (entity.getId() == null) {  //如果为null就会生成一个Id
        	//着重看这个方法
            entity.setId(nextId());
        }
        T processedEntity = preProcess(entity);
        if (processedEntity != null) {
            allRules.put(processedEntity.getId(), processedEntity);
            machineRules.computeIfAbsent(MachineInfo.of(processedEntity.getApp(), processedEntity.getIp(),
                processedEntity.getPort()), e -> new ConcurrentHashMap<>(32))
                .put(processedEntity.getId(), processedEntity);
            appRules.computeIfAbsent(processedEntity.getApp(), v -> new ConcurrentHashMap<>(32))
                .put(processedEntity.getId(), processedEntity);
        }

        return processedEntity;
    }
	//使用的是java jdk的Atomiclong自增,初始化0,也就是说每次应用重启都会把这个清空,从0开始
    private static AtomicLong ids = new AtomicLong(0);

    @Override
    protected long nextId() {
        return ids.incrementAndGet();
    }

现在是不是已经明白了呢,我们看规则新增方法(找到 /new.json API):

Date date = new Date();
entity.setGmtCreate(date);
entity.setGmtModified(date);

try {
    //保存至内存
	entity = repository.save(entity);
} catch (Throwable throwable) {
	logger.error("add gateway api error:", throwable);
    return Result.ofThrowable(-1, throwable);
}

try {
	publishApis(app, ip, port);
} catch (Exception e) {
	e.printStackTrace();
	logger.warn("publish gateway apis fail after add");
}
    private void publishApis(String app, String ip, Integer port) throws Exception {
    	//从内存Map获取规则列表
        List<ApiDefinitionEntity> apis = repository.findAllByMachine(MachineInfo.of(app, ip, port));
        //推送至配置中心
        apiPublisher.publish(app, apis);
    }

也就是说,推送到配置中心的时候是整个对象,包含AtomicLong生成的Id。

举个例子,现在就是创建了三个规则,Id根据AtomicLong生成为:id=1、id=2、id=3,那么推送去配置中心也会把Id同步过去,当Sentinel控制台重启,AtomicLong清零了,那么你新增规则的时候将会重新自增id=1,就会将之前的持久化好的id为1的覆盖了。

改造,其实就是就修改实现类:

    /**
     * 如果内存中的Id和规则中最大值的Id不相等,则以规则列表中的最大值Id为初始值
     * @param id
     */
    public void setMaxID(long id) {
        if(id != ids.get()) {
            ids = new AtomicLong(id);
        }
    }

/list.json API添加

List<ApiDefinitionEntity> apis = apiProvider.getRules(app);
repository.saveAll(apis);
if(!CollectionUtils.isEmpty(apis)) {
	//获取最大值Id
	Long id = apis.stream().max(Comparator.comparing(ApiDefinitionEntity::getId)).get().getId();
	repository.setMaxID(id);
}
return Result.ofSuccess(apis);

这是我的方法解决,如果能帮助到你,请三连,当然你们有更好的方法请在评论区讨论。
我还是会撩头发的程序猿~

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值