113 lines
3.8 KiB
C#
113 lines
3.8 KiB
C#
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<SystemInfo> GetClientInfoAsync(string machineId)
|
||
{
|
||
var json = await _db.StringGetAsync($"{Prefix}{machineId}");
|
||
return json.HasValue ? JsonConvert.DeserializeObject<SystemInfo>(json) : null;
|
||
}
|
||
|
||
// 获取所有客户端信息
|
||
public async Task<List<SystemInfo>> GetAllClientsAsync()
|
||
{
|
||
var endpoints = _redis.GetEndPoints();
|
||
var server = _redis.GetServer(endpoints.First());
|
||
var keys = server.Keys(pattern: $"{Prefix}*").ToArray();
|
||
|
||
var results = new List<SystemInfo>();
|
||
foreach (var key in keys)
|
||
{
|
||
var json = await _db.StringGetAsync(key);
|
||
if (json.HasValue)
|
||
{
|
||
results.Add(JsonConvert.DeserializeObject<SystemInfo>(json));
|
||
}
|
||
}
|
||
return results;
|
||
}
|
||
|
||
// 删除客户端信息
|
||
public async Task<bool> 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<Task>
|
||
{
|
||
// 刷新客户端信息过期时间(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<List<string>> 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<bool> IsClientActiveAsync(string machineId)
|
||
{
|
||
return await _db.KeyExistsAsync($"{HeartbeatPrefix}{machineId}");
|
||
}
|
||
|
||
public void Dispose()
|
||
{
|
||
_redis?.Dispose();
|
||
}
|
||
}
|
||
}
|