using Hopetry.Provider; using Newtonsoft.Json; using StackExchange.Redis; namespace Hopetry.Services { public class RedisService : IDisposable { private readonly ConnectionMultiplexer _redis; private readonly IDatabase _db; private const string Prefix = "client:"; public RedisService(string connectionString) { var config = ConfigurationOptions.Parse(connectionString); config.AbortOnConnectFail = false; config.ConnectTimeout = 5000; _redis = ConnectionMultiplexer.Connect(config); _db = _redis.GetDatabase(); } // 存储客户端信息(自动过期30天) public async Task StoreClientInfoAsync(SystemInfo info) { var key = $"{Prefix}{info.MachineId}"; var json = JsonConvert.SerializeObject(info); await _db.StringSetAsync(key, json, TimeSpan.FromMinutes(5)); } // 获取单个客户端信息 public async Task GetClientInfoAsync(string machineId) { var json = await _db.StringGetAsync($"{Prefix}{machineId}"); return json.HasValue ? JsonConvert.DeserializeObject(json) : null; } // 获取所有客户端信息 public async Task> GetAllClientsAsync() { var endpoints = _redis.GetEndPoints(); var server = _redis.GetServer(endpoints.First()); var keys = server.Keys(pattern: $"{Prefix}*").ToArray(); var results = new List(); foreach (var key in keys) { var json = await _db.StringGetAsync(key); if (json.HasValue) { results.Add(JsonConvert.DeserializeObject(json)); } } return results; } // 删除客户端信息 public async Task DeleteClientAsync(string machineId) { return await _db.KeyDeleteAsync($"{Prefix}{machineId}"); } // 新增心跳键前缀 private const string HeartbeatPrefix = "heartbeat:"; // 更新客户端心跳(核心新增方法) public async Task UpdateClientHeartbeatAsync(string machineId) { // 同时更新两个键的过期时间: // 1. 客户端信息键(保持长期有效) // 2. 心跳状态键(短期存活检测) var tasks = new List { // 刷新客户端信息过期时间(30天) _db.KeyExpireAsync($"{Prefix}{machineId}", TimeSpan.FromMinutes(5)), // 设置/更新心跳状态(带5分钟过期) _db.StringSetAsync( $"{HeartbeatPrefix}{machineId}", DateTime.UtcNow.ToString("o"), expiry: TimeSpan.FromMinutes(5) ) }; await Task.WhenAll(tasks); } // 获取所有活跃客户端ID(基于心跳键) public async Task> GetActiveClientIdsAsync() { var endpoints = _redis.GetEndPoints(); var server = _redis.GetServer(endpoints.First()); // 注意:生产环境应改用SCAN var heartbeatKeys = server.Keys(pattern: $"{HeartbeatPrefix}*"); return heartbeatKeys .Select(k => k.ToString().Substring(HeartbeatPrefix.Length)) .ToList(); } // 检查客户端是否活跃 public async Task IsClientActiveAsync(string machineId) { return await _db.KeyExistsAsync($"{HeartbeatPrefix}{machineId}"); } public void Dispose() { _redis?.Dispose(); } } }