FieldWorkClient/Services/RedisService.cs

113 lines
3.8 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

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();
}
}
}