diff --git a/src/AgileConfig.Server.Apisite/AgileConfig.Server.Apisite.csproj b/src/AgileConfig.Server.Apisite/AgileConfig.Server.Apisite.csproj
index ea552e2a..3d9585a5 100644
--- a/src/AgileConfig.Server.Apisite/AgileConfig.Server.Apisite.csproj
+++ b/src/AgileConfig.Server.Apisite/AgileConfig.Server.Apisite.csproj
@@ -3,11 +3,11 @@
net8.0
InProcess
- 1.9.15
- 1.9.15
- 1.9.15
+ 1.10.0
+ 1.10.0
+ 1.10.0
Linux
- 1.9.15
+ 1.10.0
kklldog
kklldog
diff --git a/src/AgileConfig.Server.Apisite/Controllers/api/ConfigController.cs b/src/AgileConfig.Server.Apisite/Controllers/api/ConfigController.cs
index 31f47b90..802d7b0a 100644
--- a/src/AgileConfig.Server.Apisite/Controllers/api/ConfigController.cs
+++ b/src/AgileConfig.Server.Apisite/Controllers/api/ConfigController.cs
@@ -9,6 +9,7 @@
using AgileConfig.Server.Apisite.Models.Mapping;
using AgileConfig.Server.Data.Entity;
using AgileConfig.Server.IService;
+using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Caching.Memory;
@@ -60,24 +61,32 @@ public async Task>> GetAppConfig(string appId, [F
}
var cacheKey = $"ConfigController_AppConfig_{appId}_{env.Value}";
- List configs = null;
- _cacheMemory?.TryGetValue(cacheKey, out configs);
- if (configs != null)
+ AppConfigsCache cache = null;
+ _cacheMemory?.TryGetValue(cacheKey, out cache);
+
+ if (cache == null)
{
- return configs;
- }
+ cache = new AppConfigsCache();
- var appConfigs = await _configService.GetPublishedConfigsByAppIdWithInheritanced(appId, env.Value);
- var vms = appConfigs.Select(x => x.ToApiConfigVM()).ToList();
+ var publishTimelineId = await _configService.GetLastPublishTimelineVirtualIdAsync(appId, env.Value);
+ var appConfigs = await _configService.GetPublishedConfigsByAppIdWithInheritance(appId, env.Value);
+ var vms = appConfigs.Select(x => x.ToApiConfigVM()).ToList();
- //增加5s的缓存,防止同一个app同时启动造成db的压力过大
- var cacheOp = new MemoryCacheEntryOptions()
- .SetAbsoluteExpiration(TimeSpan.FromSeconds(5));
- _cacheMemory?.Set(cacheKey, vms, cacheOp);
+ cache.Key = cacheKey;
+ cache.Configs = vms;
+ cache.VirtualId = publishTimelineId;
+
+ //cache 5 seconds to avoid too many db query
+ var cacheOp = new MemoryCacheEntryOptions()
+ .SetAbsoluteExpiration(TimeSpan.FromSeconds(5));
+ _cacheMemory?.Set(cacheKey, cache, cacheOp);
+ }
+
+ Response?.Headers?.Append("publish-time-line-id", cache.VirtualId);
_meterService.PullAppConfigCounter?.Add(1, new("appId", appId), new("env", env));
- return vms;
+ return cache.Configs;
}
///
diff --git a/src/AgileConfig.Server.Apisite/Controllers/api/Models/ApiConfigVM.cs b/src/AgileConfig.Server.Apisite/Controllers/api/Models/ApiConfigVM.cs
index ee21cc94..ced6e0d4 100644
--- a/src/AgileConfig.Server.Apisite/Controllers/api/Models/ApiConfigVM.cs
+++ b/src/AgileConfig.Server.Apisite/Controllers/api/Models/ApiConfigVM.cs
@@ -1,9 +1,6 @@
-using AgileConfig.Server.Apisite.Models;
+using System.Collections.Generic;
+using AgileConfig.Server.Apisite.Models;
using AgileConfig.Server.Data.Entity;
-using System;
-using System.Collections.Generic;
-using System.Linq;
-using System.Threading.Tasks;
namespace AgileConfig.Server.Apisite.Controllers.api.Models
{
@@ -53,6 +50,16 @@ public class ApiConfigVM : IAppIdModel
public string Description { get; set; }
}
+
+ public class AppConfigsCache
+ {
+ public string Key { get; set; }
+
+ public string VirtualId { get; set; }
+
+ public List Configs { get; set; }
+ }
+
public static class ApiConfigVMExtension
{
public static ConfigVM ToConfigVM(this ApiConfigVM model)
diff --git a/src/AgileConfig.Server.Apisite/Websocket/MessageHandlers/MessageHandler.cs b/src/AgileConfig.Server.Apisite/Websocket/MessageHandlers/MessageHandler.cs
index 833ef1dd..53440cf6 100644
--- a/src/AgileConfig.Server.Apisite/Websocket/MessageHandlers/MessageHandler.cs
+++ b/src/AgileConfig.Server.Apisite/Websocket/MessageHandlers/MessageHandler.cs
@@ -19,17 +19,19 @@ internal class MessageHandler : IMessageHandler
private readonly IConfigService _configService;
private readonly IRegisterCenterService _registerCenterService;
private readonly IServiceInfoService _serviceInfoService;
-
+
+ private int ClientVersion { get; set; }
+
public MessageHandler(
- IConfigService configService,
- IRegisterCenterService registerCenterService,
+ IConfigService configService,
+ IRegisterCenterService registerCenterService,
IServiceInfoService serviceInfoService)
{
_configService = configService;
_registerCenterService = registerCenterService;
_serviceInfoService = serviceInfoService;
}
-
+
public bool Hit(HttpRequest request)
{
var ver = request.Headers["client-v"];
@@ -40,6 +42,8 @@ public bool Hit(HttpRequest request)
if (int.TryParse(ver.ToString().Replace(".", ""), out var verInt))
{
+ ClientVersion = verInt;
+
return verInt >= 160;
}
@@ -63,12 +67,14 @@ public async Task Handle(string message, HttpRequest request, WebsocketClient cl
appId = HttpUtility.UrlDecode(appId);
var env = request.Headers["env"].ToString();
ISettingService.IfEnvEmptySetDefault(ref env);
- var md5 = await _configService.AppPublishedConfigsMd5CacheWithInheritanced(appId, env);
+
+ var data = await GetCPingData(appId, env);
+
await SendMessage(client.Client, JsonConvert.SerializeObject(new WebsocketAction()
{
Action = ActionConst.Ping,
Module = ActionModule.ConfigCenter,
- Data = md5
+ Data = data
}));
}
else if (message.StartsWith("s:ping:"))
@@ -98,4 +104,22 @@ await SendMessage(client.Client, JsonConvert.SerializeObject(new WebsocketAction
await SendMessage(client.Client, "0");
}
}
+
+ private async Task GetCPingData(string appId, string env)
+ {
+ if (ClientVersion <= 176)
+ {
+ // 1.7.6及以前的版本,返回V:md5
+ var md5 = await _configService.AppPublishedConfigsMd5CacheWithInheritance(appId, env);
+
+ return md5;
+ }
+ else
+ {
+ // 1.7.7及以后的版本,返回 publish time line id
+ var publishTimeLineId = await _configService.GetLastPublishTimelineVirtualIdAsyncWithCache(appId, env);
+
+ return publishTimeLineId;
+ }
+ }
}
\ No newline at end of file
diff --git a/src/AgileConfig.Server.Apisite/Websocket/MessageHandlers/OldMessageHandler.cs b/src/AgileConfig.Server.Apisite/Websocket/MessageHandlers/OldMessageHandler.cs
index e7d56f37..e2fe38f4 100644
--- a/src/AgileConfig.Server.Apisite/Websocket/MessageHandlers/OldMessageHandler.cs
+++ b/src/AgileConfig.Server.Apisite/Websocket/MessageHandlers/OldMessageHandler.cs
@@ -46,7 +46,7 @@ public async Task Handle(string message, HttpRequest request, WebsocketClient cl
var appId = request.Headers["appid"];
var env = request.Headers["env"].ToString();
env = ISettingService.IfEnvEmptySetDefault(ref env);
- var md5 = await _configService.AppPublishedConfigsMd5CacheWithInheritanced(appId, env);
+ var md5 = await _configService.AppPublishedConfigsMd5CacheWithInheritance(appId, env);
await SendMessage(client.Client, $"V:{md5}");
}
else
diff --git a/src/AgileConfig.Server.Data.Abstraction/IPublishTimelineRepository.cs b/src/AgileConfig.Server.Data.Abstraction/IPublishTimelineRepository.cs
index 8c6154fb..606115fd 100644
--- a/src/AgileConfig.Server.Data.Abstraction/IPublishTimelineRepository.cs
+++ b/src/AgileConfig.Server.Data.Abstraction/IPublishTimelineRepository.cs
@@ -4,5 +4,6 @@ namespace AgileConfig.Server.Data.Abstraction
{
public interface IPublishTimelineRepository : IRepository
{
+ Task GetLastPublishTimelineNodeIdAsync(string appId, string env);
}
}
diff --git a/src/AgileConfig.Server.Data.Repository.Freesql/PublishTimelineRepository.cs b/src/AgileConfig.Server.Data.Repository.Freesql/PublishTimelineRepository.cs
index d9ac0b93..4361f343 100644
--- a/src/AgileConfig.Server.Data.Repository.Freesql/PublishTimelineRepository.cs
+++ b/src/AgileConfig.Server.Data.Repository.Freesql/PublishTimelineRepository.cs
@@ -12,5 +12,15 @@ public PublishTimelineRepository(IFreeSql freeSql) : base(freeSql)
{
this.freeSql = freeSql;
}
+
+ public async Task GetLastPublishTimelineNodeIdAsync(string appId, string env)
+ {
+ var node = await freeSql.Select()
+ .Where(x => x.AppId == appId && x.Env == env)
+ .OrderByDescending(x => x.Version)
+ .FirstAsync();
+
+ return node?.Id;
+ }
}
}
diff --git a/src/AgileConfig.Server.Data.Repository.Mongodb/PublishTimelineRepository.cs b/src/AgileConfig.Server.Data.Repository.Mongodb/PublishTimelineRepository.cs
index 402b1247..531036e6 100644
--- a/src/AgileConfig.Server.Data.Repository.Mongodb/PublishTimelineRepository.cs
+++ b/src/AgileConfig.Server.Data.Repository.Mongodb/PublishTimelineRepository.cs
@@ -1,6 +1,7 @@
-namespace AgileConfig.Server.Data.Repository.Mongodb;
+
+namespace AgileConfig.Server.Data.Repository.Mongodb;
-public class PublishTimelineRepository: MongodbRepository, IPublishTimelineRepository
+public class PublishTimelineRepository : MongodbRepository, IPublishTimelineRepository
{
public PublishTimelineRepository(string? connectionString) : base(connectionString)
{
@@ -10,4 +11,11 @@ public PublishTimelineRepository(string? connectionString) : base(connectionStri
public PublishTimelineRepository(IConfiguration configuration) : base(configuration)
{
}
+
+ public async Task GetLastPublishTimelineNodeIdAsync(string appId, string env)
+ {
+ var nodes = await this.QueryPageAsync(x => x.AppId == appId && x.Env == env, 1, 1, nameof(PublishTimeline.Version), "DESC");
+
+ return nodes?.FirstOrDefault()?.Id;
+ }
}
\ No newline at end of file
diff --git a/src/AgileConfig.Server.IService/IConfigService.cs b/src/AgileConfig.Server.IService/IConfigService.cs
index 2e7dd22c..302cf5de 100644
--- a/src/AgileConfig.Server.IService/IConfigService.cs
+++ b/src/AgileConfig.Server.IService/IConfigService.cs
@@ -36,13 +36,13 @@ public interface IConfigService: IDisposable
///
///
///
- Task> GetPublishedConfigsByAppIdWithInheritanced(string appId, string env);
+ Task> GetPublishedConfigsByAppIdWithInheritance(string appId, string env);
///
/// 获取app的配置项继承的app配置合并进来转换为字典
///
///
///
- Task> GetPublishedConfigsByAppIdWithInheritanced_Dictionary(string appId, string env);
+ Task> GetPublishedConfigsByAppIdWithInheritance_Dictionary(string appId, string env);
Task AddAsync(Config config, string env);
Task AddRangeAsync(List configs, string env);
@@ -77,7 +77,7 @@ public interface IConfigService: IDisposable
///
///
///
- Task AppPublishedConfigsMd5WithInheritanced(string appId, string env);
+ Task AppPublishedConfigsMd5WithInheritance(string appId, string env);
///
/// 计算已发布配置项的MD5进行缓存
@@ -91,7 +91,7 @@ public interface IConfigService: IDisposable
///
///
///
- Task AppPublishedConfigsMd5CacheWithInheritanced(string appId, string env);
+ Task AppPublishedConfigsMd5CacheWithInheritance(string appId, string env);
///
/// 构造key
@@ -213,5 +213,10 @@ public interface IConfigService: IDisposable
/// clear all cache
///
void ClearCache();
+
+ Task GetLastPublishTimelineVirtualIdAsync(string appId, string env);
+
+ Task GetLastPublishTimelineVirtualIdAsyncWithCache(string appId, string env);
+
}
}
diff --git a/src/AgileConfig.Server.Service/ConfigService.cs b/src/AgileConfig.Server.Service/ConfigService.cs
index d5dd4632..4ee3cbae 100644
--- a/src/AgileConfig.Server.Service/ConfigService.cs
+++ b/src/AgileConfig.Server.Service/ConfigService.cs
@@ -308,9 +308,14 @@ private string AppPublishedConfigsMd5CacheKey(string appId, string env)
return $"ConfigService_AppPublishedConfigsMd5Cache_{appId}_{env}";
}
- private string AppPublishedConfigsMd5CacheKeyWithInheritanced(string appId, string env)
+ private string AppPublishedConfigsMd5CacheKeyWithInheritance(string appId, string env)
{
- return $"ConfigService_AppPublishedConfigsMd5CacheWithInheritanced_{appId}_{env}";
+ return $"ConfigService_AppPublishedConfigsMd5CacheWithInheritance_{appId}_{env}";
+ }
+
+ private string AppPublishTimelineVirtualIdCacheKey(string appId, string env)
+ {
+ return $"ConfigService_AppPublishTimelineVirtualIdWithCache_{appId}_{env}";
}
private void ClearAppPublishedConfigsMd5Cache(string appId, string env)
@@ -319,9 +324,15 @@ private void ClearAppPublishedConfigsMd5Cache(string appId, string env)
_memoryCache?.Remove(cacheKey);
}
- private void ClearAppPublishedConfigsMd5CacheWithInheritanced(string appId, string env)
+ private void ClearAppPublishedConfigsMd5CacheWithInheritance(string appId, string env)
+ {
+ var cacheKey = AppPublishedConfigsMd5CacheKeyWithInheritance(appId, env);
+ _memoryCache?.Remove(cacheKey);
+ }
+
+ private void ClearAppPublishTimelineVirtualIdCache(string appId, string env)
{
- var cacheKey = AppPublishedConfigsMd5CacheKeyWithInheritanced(appId, env);
+ var cacheKey = AppPublishTimelineVirtualIdCacheKey(appId, env);
_memoryCache?.Remove(cacheKey);
}
@@ -338,8 +349,10 @@ public async Task AddRangeAsync(List configs, string env)
using var repository = _configRepositoryAccessor(env);
await repository.InsertAsync(configs);
- ClearAppPublishedConfigsMd5Cache(configs.First().AppId, env);
- ClearAppPublishedConfigsMd5CacheWithInheritanced(configs.First().AppId, env);
+ var appId = configs.First().AppId;
+ ClearAppPublishedConfigsMd5Cache(appId,env);
+ ClearAppPublishedConfigsMd5CacheWithInheritance(appId, env);
+ ClearAppPublishTimelineVirtualIdCache(appId, env);
return true;
}
@@ -349,9 +362,9 @@ public async Task AddRangeAsync(List configs, string env)
///
///
///
- public async Task> GetPublishedConfigsByAppIdWithInheritanced(string appId, string env)
+ public async Task> GetPublishedConfigsByAppIdWithInheritance(string appId, string env)
{
- var configs = await GetPublishedConfigsByAppIdWithInheritanced_Dictionary(appId, env);
+ var configs = await GetPublishedConfigsByAppIdWithInheritance_Dictionary(appId, env);
return configs.Values.ToList();
}
@@ -361,7 +374,7 @@ public async Task> GetPublishedConfigsByAppIdWithInheritanced(strin
///
///
///
- public async Task> GetPublishedConfigsByAppIdWithInheritanced_Dictionary(
+ public async Task> GetPublishedConfigsByAppIdWithInheritance_Dictionary(
string appId, string env)
{
var apps = new List();
@@ -401,9 +414,9 @@ public async Task> GetPublishedConfigsByAppIdWithInhe
return configs;
}
- public async Task AppPublishedConfigsMd5WithInheritanced(string appId, string env)
+ public async Task AppPublishedConfigsMd5WithInheritance(string appId, string env)
{
- var configs = await GetPublishedConfigsByAppIdWithInheritanced(appId, env);
+ var configs = await GetPublishedConfigsByAppIdWithInheritance(appId, env);
var keyStr = string.Join('&', configs.Select(c => GenerateKey(c)).ToArray().OrderBy(k => k, StringComparer.Ordinal));
var valStr = string.Join('&', configs.Select(c => c.Value).ToArray().OrderBy(v => v, StringComparer.Ordinal));
@@ -417,15 +430,15 @@ public async Task AppPublishedConfigsMd5WithInheritanced(string appId, s
///
///
///
- public async Task AppPublishedConfigsMd5CacheWithInheritanced(string appId, string env)
+ public async Task AppPublishedConfigsMd5CacheWithInheritance(string appId, string env)
{
- var cacheKey = AppPublishedConfigsMd5CacheKeyWithInheritanced(appId, env);
+ var cacheKey = AppPublishedConfigsMd5CacheKeyWithInheritance(appId, env);
if (_memoryCache != null && _memoryCache.TryGetValue(cacheKey, out string md5))
{
return md5;
}
- md5 = await AppPublishedConfigsMd5WithInheritanced(appId, env);
+ md5 = await AppPublishedConfigsMd5WithInheritance(appId, env);
var cacheOp = new MemoryCacheEntryOptions()
.SetAbsoluteExpiration(TimeSpan.FromSeconds(60));
@@ -614,7 +627,8 @@ public void Dispose()
await uow?.SaveChangesAsync();
ClearAppPublishedConfigsMd5Cache(appId, env);
- ClearAppPublishedConfigsMd5CacheWithInheritanced(appId, env);
+ ClearAppPublishedConfigsMd5CacheWithInheritance(appId, env);
+ ClearAppPublishTimelineVirtualIdCache(appId, env);
return (true, publishTimelineNode.Id);
}
@@ -777,7 +791,8 @@ public async Task RollbackAsync(string publishTimelineId, string env)
await uow?.SaveChangesAsync();
ClearAppPublishedConfigsMd5Cache(appId, env);
- ClearAppPublishedConfigsMd5CacheWithInheritanced(appId, env);
+ ClearAppPublishedConfigsMd5CacheWithInheritance(appId, env);
+ ClearAppPublishTimelineVirtualIdCache(appId,env);
return true;
}
@@ -1077,5 +1092,61 @@ public async Task SaveKvListAsync(string kvString, string appId, string en
return ("", key);
}
+
+ ///
+ /// Generate the virtual id representing the last publish timeline node of the app and its inherited apps.
+ ///
+ ///
+ ///
+ ///
+ public async Task GetLastPublishTimelineVirtualIdAsync(string appId, string env)
+ {
+ using var publishTimelineRepository = _publishTimelineRepositoryAccsssor(env);
+
+ var apps = new List();
+ var inheritanceApps = await _appService.GetInheritancedAppsAsync(appId);
+ for (int i = 0; i < inheritanceApps.Count; i++)
+ {
+ if (inheritanceApps[i].Enabled)
+ {
+ apps.Add(inheritanceApps[i].Id as string); //后继承的排在后面
+ }
+ }
+
+ apps.Add(appId); //本应用放在最后
+
+ var ids = new List();
+
+ foreach (var app in apps)
+ {
+ var id = await publishTimelineRepository.GetLastPublishTimelineNodeIdAsync(app, env);
+ ids.Add(id);
+ }
+
+ return string.Join('|', ids);
+ }
+
+ ///
+ /// Generate the virtual id representing the last publish timeline node of the app and its inherited apps, with cache.
+ ///
+ ///
+ ///
+ ///
+ public async Task GetLastPublishTimelineVirtualIdAsyncWithCache(string appId, string env)
+ {
+ var cacheKey = AppPublishTimelineVirtualIdCacheKey(appId, env);
+ if (_memoryCache != null && _memoryCache.TryGetValue(cacheKey, out string vId))
+ {
+ return vId;
+ }
+
+ vId = await GetLastPublishTimelineVirtualIdAsync(appId, env);
+
+ var cacheOp = new MemoryCacheEntryOptions()
+ .SetAbsoluteExpiration(TimeSpan.FromSeconds(60));
+ _memoryCache?.Set(cacheKey, vId, cacheOp);
+
+ return vId;
+ }
}
}
\ No newline at end of file
diff --git a/test/AgileConfig.Server.ServiceTests/sqlite/ConfigServiceTests.cs b/test/AgileConfig.Server.ServiceTests/sqlite/ConfigServiceTests.cs
index 91eff636..179d2b4f 100644
--- a/test/AgileConfig.Server.ServiceTests/sqlite/ConfigServiceTests.cs
+++ b/test/AgileConfig.Server.ServiceTests/sqlite/ConfigServiceTests.cs
@@ -860,7 +860,7 @@ public async Task GetPublishedConfigsByAppIdWithInheritanced_DictionaryTest()
await _service.Publish(app1.Id, new string[] { }, "", "", env);
await _service.Publish(app.Id, new string[] { }, "", "", env);
- var dict = await _service.GetPublishedConfigsByAppIdWithInheritanced_Dictionary(app.Id, env);
+ var dict = await _service.GetPublishedConfigsByAppIdWithInheritance_Dictionary(app.Id, env);
Assert.IsNotNull(dict);
Assert.AreEqual(4, dict.Keys.Count);
@@ -907,7 +907,7 @@ public async Task GetPublishedConfigsByAppIdWithInheritanced_DictionaryTest()
await _service.Publish(app1.Id, new string[] { }, "", "", env);
await _service.Publish(app.Id, new string[] { }, "", "", env);
- dict = await _service.GetPublishedConfigsByAppIdWithInheritanced_Dictionary(app.Id, env);
+ dict = await _service.GetPublishedConfigsByAppIdWithInheritance_Dictionary(app.Id, env);
Assert.IsNotNull(dict);
Assert.AreEqual(5, dict.Keys.Count);
@@ -949,7 +949,7 @@ public async Task GetPublishedConfigsByAppIdWithInheritanced_DictionaryTest()
await _service.AddAsync(source6, env);
await _service.Publish(app2.Id, new string[] { }, "", "", env); // 发布app 003
- dict = await _service.GetPublishedConfigsByAppIdWithInheritanced_Dictionary(app.Id, env);
+ dict = await _service.GetPublishedConfigsByAppIdWithInheritance_Dictionary(app.Id, env);
Assert.IsNotNull(dict);
Assert.AreEqual(4, dict.Keys.Count);
diff --git a/test/ApiSiteTests/TestApiConfigController.cs b/test/ApiSiteTests/TestApiConfigController.cs
index 23ffc691..f1b387db 100644
--- a/test/ApiSiteTests/TestApiConfigController.cs
+++ b/test/ApiSiteTests/TestApiConfigController.cs
@@ -56,7 +56,7 @@ List newConfigs()
var configService = new Mock();
//configService.Setup(s => s.GetPublishedConfigsAsync("001"))
// .ReturnsAsync(newConfigs);
- configService.Setup(s => s.GetPublishedConfigsByAppIdWithInheritanced(It.IsAny(), It.IsAny()))
+ configService.Setup(s => s.GetPublishedConfigsByAppIdWithInheritance(It.IsAny(), It.IsAny()))
.ReturnsAsync(newConfigs);
IMemoryCache memoryCache = null;