VdaStrictBindingStartupGuardService.cs
2.58 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Rcs.Domain.Settings;
using StackExchange.Redis;
namespace Rcs.Infrastructure.Services;
/// <summary>
/// VDA5050 严格 binding 切换启动守卫。
/// 若检测到停机切换前遗留的旧路径缓存/锁数据,则直接阻止服务启动。
/// </summary>
public sealed class VdaStrictBindingStartupGuardService : IHostedService
{
private readonly ILogger<VdaStrictBindingStartupGuardService> _logger;
private readonly IConnectionMultiplexer _redis;
private readonly AppSettings _settings;
public VdaStrictBindingStartupGuardService(
ILogger<VdaStrictBindingStartupGuardService> logger,
IConnectionMultiplexer redis,
IOptions<AppSettings> settings)
{
_logger = logger;
_redis = redis;
_settings = settings.Value;
}
public Task StartAsync(CancellationToken cancellationToken)
{
var endPoints = _redis.GetEndPoints();
if (endPoints.Length == 0)
{
_logger.LogWarning("[VDA严格守卫] 未发现 Redis 端点,跳过严格 binding 启动检查。");
return Task.CompletedTask;
}
var patterns = new[]
{
$"{_settings.Redis.KeyPrefixes.VdaPath}:*",
$"{_settings.Redis.KeyPrefixes.NodeLockPrefix}:*",
$"{_settings.Redis.KeyPrefixes.EdgeLockPrefix}:*",
$"{_settings.Redis.KeyPrefixes.RobotLockResourcesPrefix}:*"
};
foreach (var endPoint in endPoints)
{
cancellationToken.ThrowIfCancellationRequested();
var server = _redis.GetServer(endPoint);
if (!server.IsConnected || server.IsReplica)
{
continue;
}
foreach (var pattern in patterns)
{
var leakedKeys = server.Keys(pattern: pattern, pageSize: 50)
.Take(5)
.Select(key => key.ToString())
.ToList();
if (leakedKeys.Count == 0)
{
continue;
}
var message =
$"[VDA严格守卫] 检测到停机切换前遗留的 Redis 键,拒绝启动。Pattern={pattern}, SampleKeys={string.Join(",", leakedKeys)}";
_logger.LogCritical(message);
throw new InvalidOperationException(message);
}
}
return Task.CompletedTask;
}
public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask;
}