本文主要研究一下nacos config的deleteConfig

ConfigController

nacos-1.1.3/config/src/main/java/com/alibaba/nacos/config/server/controller/ConfigController.java

@Controller
@RequestMapping(Constants.CONFIG_CONTROLLER_PATH)
public class ConfigController {

    private static final Logger log = LoggerFactory.getLogger(ConfigController.class);

    private static final String NAMESPACE_PUBLIC_KEY = "public";

    public static final String EXPORT_CONFIG_FILE_NAME = "nacos_config_export_";

    public static final String EXPORT_CONFIG_FILE_NAME_EXT = ".zip";

    public static final String EXPORT_CONFIG_FILE_NAME_DATE_FORMAT = "yyyy-MM-dd HH:mm:ss";

    private final transient ConfigServletInner inner;

    private final transient PersistService persistService;

    private final transient ConfigSubService configSubService;

    //......

    /**
     * 同步删除某个dataId下面所有的聚合前数据
     *
     * @throws NacosException
     */
    @RequestMapping(method = RequestMethod.DELETE)
    @ResponseBody
    public Boolean deleteConfig(HttpServletRequest request, HttpServletResponse response,
                                @RequestParam("dataId") String dataId, //
                                @RequestParam("group") String group, //
                                @RequestParam(value = "tenant", required = false, defaultValue = StringUtils.EMPTY)
                                    String tenant,
                                @RequestParam(value = "tag", required = false) String tag) throws NacosException {
        ParamUtils.checkParam(dataId, group, "datumId", "rm");
        ParamUtils.checkParam(tag);
        String clientIp = RequestUtil.getRemoteIp(request);
        if (StringUtils.isBlank(tag)) {
            persistService.removeConfigInfo(dataId, group, tenant, clientIp, null);
        } else {
            persistService.removeConfigInfoTag(dataId, group, tenant, tag, clientIp, null);
        }
        final Timestamp time = TimeUtils.getCurrentTime();
        ConfigTraceService.logPersistenceEvent(dataId, group, tenant, null, time.getTime(), clientIp,
            ConfigTraceService.PERSISTENCE_EVENT_REMOVE, null);
        EventDispatcher.fireEvent(new ConfigDataChangeEvent(false, dataId, group, tenant, tag, time.getTime()));
        return true;
    }

    /**
     * @author klw
     * @Description: delete configuration based on multiple config ids
     * @Date 2019/7/5 10:26
     * @Param [request, response, dataId, group, tenant, tag]
     * @return java.lang.Boolean
     */
    @RequestMapping(params = "delType=ids", method = RequestMethod.DELETE)
    @ResponseBody
    public RestResult<Boolean> deleteConfigs(HttpServletRequest request, HttpServletResponse response,
                                 @RequestParam(value = "ids")List<Long> ids) {
        String clientIp = RequestUtil.getRemoteIp(request);
        final Timestamp time = TimeUtils.getCurrentTime();
        List<ConfigInfo> configInfoList = persistService.removeConfigInfoByIds(ids, clientIp, null);
        if(!CollectionUtils.isEmpty(configInfoList)){
            for(ConfigInfo configInfo : configInfoList) {
                ConfigTraceService.logPersistenceEvent(configInfo.getDataId(), configInfo.getGroup(),
                    configInfo.getTenant(), null, time.getTime(), clientIp,
                    ConfigTraceService.PERSISTENCE_EVENT_REMOVE, null);
                EventDispatcher.fireEvent(new ConfigDataChangeEvent(false, configInfo.getDataId(),
                    configInfo.getGroup(), configInfo.getTenant(), time.getTime()));
            }
        }
        return ResultBuilder.buildSuccessResult(true);
    }

    //......
}
  • deleteConfig方法主要是调用persistService.removeConfigInfo或者persistService.removeConfigInfoTag;deleteConfigs方法主要是调用persistService.removeConfigInfoByIds

PersistService

nacos-1.1.3/config/src/main/java/com/alibaba/nacos/config/server/service/PersistService.java

@Repository
public class PersistService {

    @Autowired
    private DynamicDataSource dynamicDataSource;

    private DataSourceService dataSourceService;

    private static final String SQL_FIND_ALL_CONFIG_INFO = "select data_id,group_id,tenant_id,app_name,content,type from config_info";

    private static final String SQL_TENANT_INFO_COUNT_BY_TENANT_ID = "select count(1) from tenant_info where tenant_id = ?";

    private static final String SQL_FIND_CONFIG_INFO_BY_IDS = "SELECT ID,data_id,group_id,tenant_id,app_name,content,md5 FROM config_info WHERE ";

    private static final String SQL_DELETE_CONFIG_INFO_BY_IDS = "DELETE FROM config_info WHERE ";

    /**
     * @author klw
     * @Description: constant variables
     */
    public static final String SPOT = ".";

    @PostConstruct
    public void init() {
        dataSourceService = dynamicDataSource.getDataSource();

        jt = getJdbcTemplate();
        tjt = getTransactionTemplate();
    }

    //......

    /**
     * 删除配置信息, 物理删除
     */
    public void removeConfigInfo(final String dataId, final String group, final String tenant, final String srcIp,
                                 final String srcUser) {
        tjt.execute(new TransactionCallback<Boolean>() {
            final Timestamp time = new Timestamp(System.currentTimeMillis());

            @Override
            public Boolean doInTransaction(TransactionStatus status) {
                try {
                    ConfigInfo configInfo = findConfigInfo(dataId, group, tenant);
                    if (configInfo != null) {
                        removeConfigInfoAtomic(dataId, group, tenant, srcIp, srcUser);
                        removeTagByIdAtomic(configInfo.getId());
                        insertConfigHistoryAtomic(configInfo.getId(), configInfo, srcIp, srcUser, time, "D");
                    }
                } catch (CannotGetJdbcConnectionException e) {
                    fatalLog.error("[db-error] " + e.toString(), e);
                    throw e;
                }
                return Boolean.TRUE;
            }
        });
    }

    /**
     * 删除配置;数据库原子操作,最小sql动作,无业务封装
     *
     * @param dataId  dataId
     * @param group   group
     * @param tenant  tenant
     * @param tag     tag
     * @param srcIp   ip
     * @param srcUser user
     */
    public void removeConfigInfoTag(final String dataId, final String group, final String tenant, final String tag,
                                    final String srcIp,
                                    final String srcUser) {
        String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant;
        String tagTmp = StringUtils.isBlank(tag) ? StringUtils.EMPTY : tag;
        try {
            jt.update("DELETE FROM config_info_tag WHERE data_id=? AND group_id=? AND tenant_id=? AND tag_id=?", dataId,
                group,
                tenantTmp, tagTmp);
        } catch (CannotGetJdbcConnectionException e) {
            fatalLog.error("[db-error] " + e.toString(), e);
            throw e;
        }
    }

    /**
     * @author klw
     * @Description: delete config info by ids
     * @Date 2019/7/5 16:45
     * @Param [ids, srcIp, srcUser]
     * @return List<ConfigInfo> deleted configInfos
     */
    public List<ConfigInfo> removeConfigInfoByIds(final List<Long> ids, final String srcIp, final String srcUser) {
        if(CollectionUtils.isEmpty(ids)){
            return null;
        }
        ids.removeAll(Collections.singleton(null));
        return tjt.execute(new TransactionCallback<List<ConfigInfo>>() {
            final Timestamp time = new Timestamp(System.currentTimeMillis());

            @Override
            public List<ConfigInfo> doInTransaction(TransactionStatus status) {
                try {
                    String idsStr = Joiner.on(",").join(ids);
                    List<ConfigInfo> configInfoList = findConfigInfosByIds(idsStr);
                    if (!CollectionUtils.isEmpty(configInfoList)) {
                        removeConfigInfoByIdsAtomic(idsStr);
                        for(ConfigInfo configInfo : configInfoList){
                            removeTagByIdAtomic(configInfo.getId());
                            insertConfigHistoryAtomic(configInfo.getId(), configInfo, srcIp, srcUser, time, "D");
                        }
                    }
                    return configInfoList;
                } catch (CannotGetJdbcConnectionException e) {
                    fatalLog.error("[db-error] " + e.toString(), e);
                    throw e;
                }
            }
        });
    }

    /**
     * 删除配置;数据库原子操作,最小sql动作,无业务封装
     *
     * @param dataId  dataId
     * @param group   group
     * @param tenant  tenant
     * @param srcIp   ip
     * @param srcUser user
     */
    private void removeConfigInfoAtomic(final String dataId, final String group, final String tenant,
                                        final String srcIp,
                                        final String srcUser) {
        String tenantTmp = StringUtils.isBlank(tenant) ? StringUtils.EMPTY : tenant;
        try {
            jt.update("DELETE FROM config_info WHERE data_id=? AND group_id=? AND tenant_id=?", dataId, group,
                tenantTmp);
        } catch (CannotGetJdbcConnectionException e) {
            fatalLog.error("[db-error] " + e.toString(), e);
            throw e;
        }
    }

    public void removeTagByIdAtomic(long id) {
        try {
            jt.update("DELETE FROM config_tags_relation WHERE id=?", id);
        } catch (CannotGetJdbcConnectionException e) {
            fatalLog.error("[db-error] " + e.toString(), e);
            throw e;
        }
    }

    /**
     * 更新变更记录;数据库原子操作,最小sql动作,无业务封装
     *
     * @param id         id
     * @param configInfo config info
     * @param srcIp      ip
     * @param srcUser    user
     * @param time       time
     * @param ops        ops type
     */
    private void insertConfigHistoryAtomic(long id, ConfigInfo configInfo, String srcIp, String srcUser,
                                           final Timestamp time, String ops) {
        String appNameTmp = StringUtils.isBlank(configInfo.getAppName()) ? StringUtils.EMPTY : configInfo.getAppName();
        String tenantTmp = StringUtils.isBlank(configInfo.getTenant()) ? StringUtils.EMPTY : configInfo.getTenant();
        final String md5Tmp = MD5.getInstance().getMD5String(configInfo.getContent());
        try {
            jt.update(
                "INSERT INTO his_config_info (id,data_id,group_id,tenant_id,app_name,content,md5,src_ip,src_user,gmt_modified,op_type) VALUES(?,?,?,?,?,?,?,?,?,?,?)",
                id, configInfo.getDataId(), configInfo.getGroup(), tenantTmp, appNameTmp, configInfo.getContent(),
                md5Tmp, srcIp, srcUser, time, ops);
        } catch (DataAccessException e) {
            fatalLog.error("[db-error] " + e.toString(), e);
            throw e;
        }
    }

    /**
     * @author klw
     * @Description: find ConfigInfo by ids
     * @Date 2019/7/5 16:37
     * @Param [ids]
     * @return java.util.List<com.alibaba.nacos.config.server.model.ConfigInfo>
     */
    public List<ConfigInfo> findConfigInfosByIds(final String ids) {
        if(StringUtils.isBlank(ids)){
            return null;
        }
        StringBuilder sql = new StringBuilder(SQL_FIND_CONFIG_INFO_BY_IDS);
        sql.append("id in (");
        List<Long> paramList = new ArrayList<>();
        String[] tagArr = ids.split(",");
        for (int i = 0; i < tagArr.length; i++) {
            if (i != 0) {
                sql.append(", ");
            }
            sql.append("?");
            paramList.add(Long.valueOf(tagArr[i]));
        }
        sql.append(") ");
        try {
            return this.jt.query(sql.toString(), paramList.toArray(), CONFIG_INFO_ROW_MAPPER);
        } catch (EmptyResultDataAccessException e) { // 表明数据不存在, 返回null
            return null;
        } catch (CannotGetJdbcConnectionException e) {
            fatalLog.error("[db-error] " + e.toString(), e);
            throw e;
        }
    }

    /**
     * @author klw
     * @Description: Delete configuration; database atomic operation, minimum SQL action, no business encapsulation
     * @Date 2019/7/5 16:39
     * @Param [id]
     * @return void
     */
    private void removeConfigInfoByIdsAtomic(final String ids) {
        if(StringUtils.isBlank(ids)){
            return;
        }
        StringBuilder sql = new StringBuilder(SQL_DELETE_CONFIG_INFO_BY_IDS);
        sql.append("id in (");
        List<Long> paramList = new ArrayList<>();
        String[] tagArr = ids.split(",");
        for (int i = 0; i < tagArr.length; i++) {
            if (i != 0) {
                sql.append(", ");
            }
            sql.append("?");
            paramList.add(Long.valueOf(tagArr[i]));
        }
        sql.append(") ");
        try {
            jt.update(sql.toString(), paramList.toArray());
        } catch (CannotGetJdbcConnectionException e) {
            fatalLog.error("[db-error] " + e.toString(), e);
            throw e;
        }
    }


    //......
}
  • removeConfigInfo方法在事务中执行removeConfigInfoAtomic、removeTagByIdAtomic、insertConfigHistoryAtomic;removeConfigInfoTag方法直接从config_info_tag表删除对应的数据;removeConfigInfoByIds方法在事务中执行findConfigInfosByIds、removeConfigInfoByIdsAtomic、removeTagByIdAtomic、insertConfigHistoryAtomic方法
  • removeConfigInfoAtomic方法直接从config_info表删除数据;removeTagByIdAtomic方法直接从config_tags_relation表删除数据;insertConfigHistoryAtomic方法往his_config_info插入数据
  • findConfigInfosByIds方法根据id从config_info表查询记录;removeConfigInfoByIdsAtomic至二级根据id从config_info表删除记录

小结

ConfigController的deleteConfig方法主要是调用persistService.removeConfigInfo或者persistService.removeConfigInfoTag;deleteConfigs方法主要是调用persistService.removeConfigInfoByIds

doc

03-05 18:03