feat: 实现配置删除通知机制(deletedKeys)
- 服务端 fetchChangedConfigs 接口新增 deletedKeys 字段,返回已删除配置的 key 列表 - Go/Node.js/Python/Spring Boot 客户端支持解析 deletedKeys 并删除本地配置 - Spring Boot 客户端新增 removeConfigs 方法,同步更新 Spring Environment
This commit is contained in:
@@ -223,6 +223,18 @@ func (c *Client) fetchChanged() error {
|
||||
delete(wrapper.Data, "latest.fetch.timestamp")
|
||||
}
|
||||
|
||||
// 处理删除的配置
|
||||
if deletedKeysStr, ok := wrapper.Data["deletedKeys"]; ok && deletedKeysStr != "" {
|
||||
deletedKeys := strings.Split(deletedKeysStr, ",")
|
||||
c.mu.Lock()
|
||||
for _, key := range deletedKeys {
|
||||
delete(c.configs, key)
|
||||
}
|
||||
c.mu.Unlock()
|
||||
log.Printf("[ConfigClient] %d configs deleted", len(deletedKeys))
|
||||
delete(wrapper.Data, "deletedKeys")
|
||||
}
|
||||
|
||||
if len(wrapper.Data) > 0 {
|
||||
changed := make(map[string]string)
|
||||
for k, v := range wrapper.Data {
|
||||
|
||||
@@ -127,6 +127,17 @@ export class ConfigClient {
|
||||
}
|
||||
delete changedData['latest.fetch.timestamp'];
|
||||
|
||||
// 处理删除的配置
|
||||
const deletedKeysStr = changedData['deletedKeys'];
|
||||
if (deletedKeysStr) {
|
||||
const deletedKeys = deletedKeysStr.split(',');
|
||||
for (const key of deletedKeys) {
|
||||
delete this.configs[key];
|
||||
}
|
||||
console.info('[ConfigClient]', deletedKeys.length, 'configs deleted');
|
||||
delete changedData['deletedKeys'];
|
||||
}
|
||||
|
||||
const changed = {};
|
||||
for (const [k, v] of Object.entries(changedData)) {
|
||||
changed[k] = v;
|
||||
|
||||
@@ -103,6 +103,15 @@ class ConfigClient:
|
||||
if ts:
|
||||
self._last_time = int(ts)
|
||||
|
||||
# 处理删除的配置
|
||||
deleted_keys_str = changed_data.pop("deletedKeys", None)
|
||||
if deleted_keys_str:
|
||||
deleted_keys = deleted_keys_str.split(",")
|
||||
with self._lock:
|
||||
for key in deleted_keys:
|
||||
self._configs.pop(key, None)
|
||||
logger.info("[ConfigClient] %d configs deleted", len(deleted_keys))
|
||||
|
||||
if changed_data:
|
||||
changed = {}
|
||||
for k, v in changed_data.items():
|
||||
|
||||
+36
@@ -415,6 +415,42 @@ public class ConfigcenterContext {
|
||||
}
|
||||
}
|
||||
|
||||
public synchronized void removeConfigs(List<String> keys) {
|
||||
for (String key : keys) {
|
||||
localProps.remove(key);
|
||||
logger.info("Config [{}] removed", key);
|
||||
}
|
||||
// Update Spring Environment property source
|
||||
if (applicationContext != null) {
|
||||
StandardEnvironment environment = (StandardEnvironment) applicationContext.getEnvironment();
|
||||
MutablePropertySources propertySources = environment.getPropertySources();
|
||||
synchronized (propertySources) {
|
||||
if (propertySources.contains(MANAGER_PROPERTY_SOURCE)) {
|
||||
Properties properties = new Properties();
|
||||
Object source = propertySources.get(MANAGER_PROPERTY_SOURCE);
|
||||
if (source instanceof MapPropertySource) {
|
||||
Map<String, Object> map = ((MapPropertySource) source).getSource();
|
||||
for (Entry<String, Object> entry : map.entrySet()) {
|
||||
if (!keys.contains(entry.getKey())) {
|
||||
properties.put(entry.getKey(), entry.getValue());
|
||||
}
|
||||
}
|
||||
} else if (source instanceof PropertiesPropertySource) {
|
||||
Map<String, Object> src = ((PropertiesPropertySource) source).getSource();
|
||||
for (Entry<String, Object> entry : src.entrySet()) {
|
||||
if (!keys.contains(entry.getKey())) {
|
||||
properties.put(entry.getKey(), entry.getValue().toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
propertySources.replace(MANAGER_PROPERTY_SOURCE, new PropertiesPropertySource(MANAGER_PROPERTY_SOURCE, properties));
|
||||
}
|
||||
}
|
||||
}
|
||||
// Refresh @Value and @ConfigurationProperties fields
|
||||
refreshDynamicConfigFields();
|
||||
}
|
||||
|
||||
private void refreshDynamicConfigFields() {
|
||||
if (applicationContext != null) {
|
||||
try {
|
||||
|
||||
+9
@@ -63,6 +63,15 @@ public class InternalConfigChangeListener {
|
||||
if (data.containsKey(LATEST_FETCH_TIMESTAMP)) {
|
||||
params.put("lastTime", Long.parseLong(data.remove(LATEST_FETCH_TIMESTAMP).toString()));
|
||||
}
|
||||
// 处理删除的配置
|
||||
if (data.containsKey("deletedKeys")) {
|
||||
String deletedKeysStr = (String) data.remove("deletedKeys");
|
||||
if (deletedKeysStr != null && !deletedKeysStr.isEmpty()) {
|
||||
String[] deletedKeys = deletedKeysStr.split(",");
|
||||
context.removeConfigs(java.util.Arrays.asList(deletedKeys));
|
||||
logger.info("[ConfigClient] {} configs deleted", deletedKeys.length);
|
||||
}
|
||||
}
|
||||
if (!data.isEmpty()) context.updateConfig(data);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
"net"
|
||||
"net/http"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
@@ -172,6 +173,32 @@ func (h *ConfigApiHandler) FetchChangedConfigs(c *gin.Context) {
|
||||
result := make(map[string]string)
|
||||
var configIds []int
|
||||
|
||||
// 查询已删除的配置(enabled=0 且 updated_at > lastTime),返回 deletedKeys
|
||||
var deletedKeys []string
|
||||
if !req.IgnoreGlobal && app.GroupId != nil {
|
||||
var deletedGlobalConfigs []model.AppConfig
|
||||
dao.Engine.Where("env = ? AND version = ? AND enabled = 0 AND group_id = ? AND (app_id is null OR app_id = 0) AND updated_at > ?",
|
||||
req.Env, version, *app.GroupId, lastUpdateTime).Find(&deletedGlobalConfigs)
|
||||
for _, cfg := range deletedGlobalConfigs {
|
||||
kvMap := h.parseService.ParseConfigToKVMap(&cfg)
|
||||
for k := range kvMap {
|
||||
deletedKeys = append(deletedKeys, k)
|
||||
}
|
||||
}
|
||||
}
|
||||
var deletedAppConfigs []model.AppConfig
|
||||
dao.Engine.Where("env = ? AND version = ? AND enabled = 0 AND app_id = ? AND updated_at > ?",
|
||||
req.Env, version, app.Id, lastUpdateTime).Find(&deletedAppConfigs)
|
||||
for _, cfg := range deletedAppConfigs {
|
||||
kvMap := h.parseService.ParseConfigToKVMap(&cfg)
|
||||
for k := range kvMap {
|
||||
deletedKeys = append(deletedKeys, k)
|
||||
}
|
||||
}
|
||||
if len(deletedKeys) > 0 {
|
||||
result["deletedKeys"] = strings.Join(deletedKeys, ",")
|
||||
}
|
||||
|
||||
// load global changed configs
|
||||
if !req.IgnoreGlobal && app.GroupId != nil {
|
||||
var globalConfigs []model.AppConfig
|
||||
|
||||
Reference in New Issue
Block a user