nacos多人发布冲突问题

前言

Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service的首字母简称,一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。

作为一个后台开发,nacos是我经常用到的一款集注册中心、配置中心于一身的平台,简单的配置启动,然后应用注册即可使用,还有简洁明了使用的UI页面,点个赞先!

贴上官网地址:
https://nacos.io/

问题及处理

问题

在使用nacos页面更改配置并发布的时候发现一个问题:

当两个人A和B都打开了某个配置的编辑页面,都进行了修改,然后点了发布,则会出现后点发布的B提交的配置生效,因为会覆盖前者A发布的版本,而此时如果没做比对或确认的话A就会没有察觉,从而导致可能的一些问题…

复现

该问题也很好复现,两个人直接按照步骤操作即可,一个人的话通过两个浏览器来模拟:

1、通过浏览器A打开nacos UI页面,找到一个配置文件,点击编辑,打开编辑页面;

nacos多人发布冲突问题_第1张图片

2、通过浏览器B打开nacos UI页面,也找到刚刚打开的那个配置文件,点击编辑,打开编辑页面;

nacos多人发布冲突问题_第2张图片

3、在浏览器A打开的nacos配置编辑页面对配置进行变更修改,然后点击发布;

nacos多人发布冲突问题_第3张图片

4、在浏览器B打开的nacos配置编辑页面也对配置进行变更修改,然后点击发布;

nacos多人发布冲突问题_第4张图片

5、再次查看nacos上这个配置文件的配置信息,会发现呈现的是浏览器B编辑发布后的结果;

nacos多人发布冲突问题_第5张图片

处理

思路:在发布之前进行MD5比对!拿到源码,本地对逻辑进行变更,然后编译打包使用…

代码修改处如下:

1、com.alibaba.nacos.config.server.model.form.ConfigForm

增加md5参数字段

package com.alibaba.nacos.config.server.model.form;

import com.alibaba.nacos.api.exception.api.NacosApiException;
import com.alibaba.nacos.api.model.v2.ErrorCode;
import com.alibaba.nacos.common.utils.StringUtils;
import org.springframework.http.HttpStatus;

import java.io.Serializable;
import java.util.Objects;

public class ConfigForm implements Serializable {
    
    // ... 代码省略

    private String md5;
    
    public ConfigForm() {
    }
    
    public ConfigForm(String dataId, String group, String namespaceId, String content, String tag, String appName,
            String srcUser, String configTags, String desc, String use, String effect, String type, String schema,
                      String md5) {
        this.dataId = dataId;
        this.group = group;
        this.namespaceId = namespaceId;
        this.content = content;
        this.tag = tag;
        this.appName = appName;
        this.srcUser = srcUser;
        this.configTags = configTags;
        this.desc = desc;
        this.use = use;
        this.effect = effect;
        this.type = type;
        this.schema = schema;
        this.md5 = md5;
    }
    
    // ... 代码省略

    public String getMd5() {
        return md5;
    }

    public void setMd5(String md5) {
        this.md5 = md5;
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (o == null || getClass() != o.getClass()) {
            return false;
        }
        ConfigForm configForm = (ConfigForm) o;
        return dataId.equals(configForm.dataId) && group.equals(configForm.group) && Objects.equals(namespaceId, configForm.namespaceId)
                && content.equals(configForm.content) && Objects.equals(tag, configForm.tag) && Objects
                .equals(appName, configForm.appName) && Objects.equals(srcUser, configForm.srcUser) && Objects
                .equals(configTags, configForm.configTags) && Objects.equals(desc, configForm.desc) && Objects
                .equals(use, configForm.use) && Objects.equals(effect, configForm.effect) && Objects
                .equals(type, configForm.type) && Objects.equals(schema, configForm.schema)
                && Objects.equals(md5, configForm.md5);
    }
    
    @Override
    public int hashCode() {
        return Objects.hash(dataId, group, namespaceId, content, tag, appName, srcUser, configTags, desc, use, effect, type,
                schema, md5);
    }
    
    @Override
    public String toString() {
        return "ConfigVo{" +
                "dataId='" + dataId + '\'' +
                ", group='" + group + '\'' +
                ", namespaceId='" + namespaceId + '\'' +
                ", content='" + content + '\'' +
                ", tag='" + tag + '\'' +
                ", appName='" + appName + '\'' +
                ", srcUser='" + srcUser + '\'' +
                ", configTags='" + configTags + '\'' +
                ", desc='" + desc + '\'' +
                ", use='" + use + '\'' +
                ", effect='" + effect + '\'' +
                ", type='" + type + '\'' +
                ", schema='" + schema + '\'' +
                ", md5='" + md5 + '\'' +
                '}';
    }

    /**
     * Validate.
     *
     * @throws NacosApiException NacosApiException.
     */
    public void validate() throws NacosApiException {
        if (StringUtils.isBlank(dataId)) {
            throw new NacosApiException(HttpStatus.BAD_REQUEST.value(), ErrorCode.PARAMETER_MISSING,
                    "Required parameter 'dataId' type String is not present");
        } else if (StringUtils.isBlank(group)) {
            throw new NacosApiException(HttpStatus.BAD_REQUEST.value(), ErrorCode.PARAMETER_MISSING,
                    "Required parameter 'group' type String is not present");
        } else if (StringUtils.isBlank(content)) {
            throw new NacosApiException(HttpStatus.BAD_REQUEST.value(), ErrorCode.PARAMETER_MISSING,
                    "Required parameter 'content' type String is not present");
        }
    }
}

2、com.alibaba.nacos.config.server.controller.ConfigController#publishConfig

添加md5入参,前端有传,直接接收

@PostMapping
    @TpsControl(pointName = "ConfigPublish")
    @Secured(action = ActionTypes.WRITE, signType = SignType.CONFIG)
    public Boolean publishConfig(HttpServletRequest request, HttpServletResponse response,
            @RequestParam(value = "dataId") String dataId,
            @RequestParam(value = "group") String group,
            @RequestParam(value = "tenant", required = false, defaultValue = StringUtils.EMPTY) String tenant,
            @RequestParam(value = "content") String content, @RequestParam(value = "tag", required = false) String tag,
            @RequestParam(value = "appName", required = false) String appName,
            @RequestParam(value = "src_user", required = false) String srcUser,
            @RequestParam(value = "config_tags", required = false) String configTags,
            @RequestParam(value = "desc", required = false) String desc,
            @RequestParam(value = "use", required = false) String use,
            @RequestParam(value = "effect", required = false) String effect,
            @RequestParam(value = "type", required = false) String type,
            @RequestParam(value = "schema", required = false) String schema,
            @RequestParam(value = "md5", required = false) String md5) throws NacosException {
        
        // ... 代码省略
        configForm.setMd5(md5);

        // ... 代码省略
       
        return configOperationService.publishConfig(configForm, configRequestInfo, encryptedDataKey);
    }

3、com.alibaba.nacos.config.server.service.ConfigOperationService#publishConfig

添加校验逻辑

public Boolean publishConfig(ConfigForm configForm, ConfigRequestInfo configRequestInfo, String encryptedDataKey)
            throws NacosException {
        
        // ... 代码省略

        ConfigInfo oldConfigInfo = configInfoPersistService.findConfigInfo(configInfo.getDataId(), configInfo.getGroup(),
                configInfo.getTenant());
        String oldConfigInfoMd5 = oldConfigInfo.getMd5();
        String curMd5 = configForm.getMd5();
        if (!StringUtils.equals(curMd5, oldConfigInfoMd5)) {
            // 传进来的上一版本md5和数据库里现在的md5不相等,表示在此之前其他用户已更新,不能直接覆盖
            throw new NacosApiException(HttpStatus.METHOD_NOT_ALLOWED.value(), ErrorCode.RESOURCE_CONFLICT, "已有其他用户更新了此配置,请先更新再修改发布!");
        }
        
        // ... 代码省略
        
        return true;
    }

4、编译打包

mvn -Prelease-nacos -Dmaven.test.skip=true clean install -U  

nacos多人发布冲突问题_第6张图片

5、拿到打包后的新包使用即可

新包路径:项目代码路径/distribution/target

nacos多人发布冲突问题_第7张图片

复测

再次测试结果符合预期!

nacos多人发布冲突问题_第8张图片

nacos多人发布冲突问题_第9张图片

nacos多人发布冲突问题_第10张图片

结语

到此这个问题就可以如愿解决了,欢迎留言交流!

如果您看到了这里,欢迎和我沟通交流!
             一个95后码农

个人博客:fy-blog

你可能感兴趣的:(java,工具,java)