ChouBox/Utile/HuanMeng.DotNetCore/Redis/RedisConnection.cs
2025-04-23 19:20:23 +08:00

337 lines
11 KiB
C#

using Microsoft.EntityFrameworkCore.Metadata.Internal;
using Microsoft.Identity.Client;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using StackExchange.Redis;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using IDatabase = StackExchange.Redis.IDatabase;
namespace HuanMeng.DotNetCore.Redis
{
/// <summary>
/// 数据库连接字符串
/// </summary>
public static class RedisConnection
{
/// <summary>
/// 数据库连接
/// </summary>
public static ConcurrentDictionary<string, IDatabase> Redis { get; set; } = new ConcurrentDictionary<string, IDatabase>();
/// <summary>
/// 数据库查询
/// </summary>
public static ConcurrentDictionary<string, IServer> RedisServer { get; set; } = new ConcurrentDictionary<string, IServer>();
/// <summary>
///
/// </summary>
/// <param name="redisConnection"></param>
/// <returns></returns>
public static IDatabase GetRedis(string redisConnection)
{
if (!Redis.TryGetValue(redisConnection, out var database))
{
var redis = ConnectionMultiplexer.Connect(redisConnection);
database = redis.GetDatabase();
//server.key
//redis.GetServer()
Redis.TryAdd(redisConnection, database);
}
return database;
}
/// <summary>
///
/// </summary>
/// <param name="redisConnection"></param>
/// <returns></returns>
public static IServer GetServer(string redisConnection)
{
if (!RedisServer.TryGetValue(redisConnection, out var server))
{
var redis = ConnectionMultiplexer.Connect(redisConnection);
var serverConn = ParseIpPortAndDatabase(redisConnection);
server = redis.GetServer(serverConn.ip, serverConn.port);
//server.key
//redis.GetServer()
RedisServer.TryAdd(redisConnection, server);
}
return server;
}
private static (string ip, int port, int database) ParseIpPortAndDatabase(string connectionString)
{
// 默认端口号和默认数据库
int defaultPort = 6379;
int defaultDatabase = 0;
if (string.IsNullOrEmpty(connectionString))
{
return ("localhost", defaultPort, defaultDatabase);
}
// 按逗号分割,获取主机部分和其他参数
var parts = connectionString.Split(',');
var hostPart = parts[0]; // 例如:"192.168.195.6:6379"
// 检查是否包含端口号
string ip;
int port;
if (hostPart.Contains(":"))
{
var hostParts = hostPart.Split(':');
ip = hostParts[0];
port = int.TryParse(hostParts[1], out int parsedPort) ? parsedPort : defaultPort;
}
else
{
// 如果没有端口号,使用默认端口
ip = hostPart;
port = defaultPort;
}
// 解析 defaultDatabase 参数
int database = defaultDatabase;
foreach (var part in parts)
{
if (part.StartsWith("defaultDatabase=", StringComparison.OrdinalIgnoreCase))
{
var dbPart = part.Split('=');
if (dbPart.Length == 2 && int.TryParse(dbPart[1], out int parsedDb))
{
database = parsedDb;
}
}
}
return (ip, port, database);
}
/// <summary>
///
/// </summary>
/// <param name="database"></param>
/// <param name="key"></param>
/// <param name="value"></param>
/// <param name="time"></param>
/// <returns></returns>
public static bool StringSetLock(this IDatabase database, string key, string value, int time = 10)
{
return database.StringSet(key, value, TimeSpan.FromSeconds(time), when: When.NotExists);
}
/// <summary>
/// 删除key
/// </summary>
/// <param name="database"></param>
/// <param name="key"></param>
/// <returns></returns>
public static async Task<int> KeysDeleteds(this IDatabase database, string key)
{
string luaScript = @"
local keys = redis.call('KEYS', ARGV[1])
if next(keys) ~= nil then
return redis.call('DEL', unpack(keys))
else
return 0
end
";
var keysDeleted = (int)await (database.ScriptEvaluateAsync(luaScript, values: new RedisValue[] { key }));
return keysDeleted;
}
/// <summary>
/// 获取一个key的对象
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="key"></param>
/// <returns></returns>
public static T? StringGet<T>(this IDatabase database, string key)
{
var value = database.StringGet(key);
// 检查值是否为空
if (!value.HasValue)
{
return default(T);
}
if (typeof(T).IsPrimitive || typeof(T) == typeof(string) || typeof(T) == typeof(decimal))
{
try
{
return (T)Convert.ChangeType(value.ToString(), typeof(T));
}
catch
{
return default; // 或抛出异常,取决于业务需求
}
}
// 将 RedisValue 转换为 T 类型
return JsonConvert.DeserializeObject<T>(value);
}
/// <summary>
/// 数据存放在redis
/// </summary>
/// <param name="database"></param>
/// <param name="key"></param>
/// <param name="value"></param>
/// <param name="expiry"></param>
/// <returns></returns>
public static async Task<bool> StringSetAsync(this IDatabase database, string key, object value, TimeSpan? expiry)
{
return await database.StringSetAsync(key, (value == null ? "" : JsonConvert.SerializeObject(value)), expiry, When.Always);
}
/// <summary>
/// 数据存放在redis
/// </summary>
/// <param name="database"></param>
/// <param name="key"></param>
/// <param name="value"></param>
/// <param name="expiry"></param>
/// <returns></returns>
public static bool StringSet(this IDatabase database, string key, object value, TimeSpan? expiry = null)
{
return database.StringSet(key, (value == null ? "" : JsonConvert.SerializeObject(value)), expiry, When.Always);
}
/// <summary>
/// 获取一个key的对象
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="key"></param>
/// <returns></returns>
public static async Task<T?> StringGetAsync<T>(this IDatabase database, string key)
{
var value = await database.StringGetAsync(key);
// 检查值是否为空
if (!value.HasValue)
{
return default(T);
}
if (typeof(T).IsPrimitive || typeof(T) == typeof(string) || typeof(T) == typeof(decimal))
{
try
{
return (T)Convert.ChangeType(value.ToString(), typeof(T));
}
catch
{
return default; // 或抛出异常,取决于业务需求
}
}
// 将 RedisValue 转换为 T 类型
return JsonConvert.DeserializeObject<T>(value);
}
/// <summary>
/// 模糊查询key
/// </summary>
/// <param name="server"></param>
/// <param name="pattern"></param>
/// <param name="pageSize"></param>
/// <returns></returns>
public static List<string> ScanKeys(this IServer server, string pattern, int pageSize = 1000)
{
var matchingKeys = server.Keys(pattern: pattern, pageSize: pageSize).Select(it => it.ToString()).ToList();
return matchingKeys;
}
/// <summary>
/// 异步模糊查询key
/// </summary>
/// <param name="server">Redis 服务器实例</param>
/// <param name="pattern">匹配的模式(支持通配符)</param>
/// <param name="pageSize">每次扫描的页大小</param>
/// <param name="database">数据库编号,默认值为 -1 表示全部数据库</param>
/// <returns>匹配的键的字符串列表</returns>
public static async Task<List<string>> ScanKeysAsync(this IServer server, string pattern, int pageSize = 1000, int database = -1)
{
var matchingKeys = new List<string>();
try
{
await foreach (var key in server.KeysAsync(database: database, pattern: pattern, pageSize: pageSize))
{
matchingKeys.Add(key.ToString());
}
}
catch (Exception ex)
{
// 可以记录日志或者处理异常
Console.WriteLine($"Error while scanning keys: {ex.Message}");
}
return matchingKeys;
}
/// <summary>
/// 模糊查询所有匹配的键的数量
/// </summary>
/// <param name="server">Redis 服务器实例</param>
/// <param name="pattern">匹配的模式(支持通配符)</param>
/// <returns>匹配的键的数量</returns>
public static int ScanKeysCount(this IServer server, string pattern, int database = -1)
{
int count = 0;
long cursor = 0; // 游标,用于记录扫描进度
do
{
// 使用 Keys 方法进行分页查询
var keys = server.Keys(database: database, cursor: cursor, pattern: pattern, pageSize: 1000).ToArray();
// 累计匹配到的键的数量
count += keys.Length;
// 如果还有更多键,则获取新的游标;否则,循环终止
cursor = keys.Length == 1000 ? 1 : 0;
} while (cursor != 0);
return count;
}
/// <summary>
/// 异步模糊查询所有匹配的键的数量
/// </summary>
/// <param name="server">Redis 服务器实例</param>
/// <param name="pattern">匹配的模式(支持通配符)</param>
/// <param name="pageSize">每次扫描的页大小</param>
/// <param name="database">数据库编号,默认为 -1 表示当前数据库</param>
/// <returns>匹配的键的数量</returns>
public static async Task<int> ScanKeysCountAsync(this IServer server, string pattern, int pageSize = 1000, int database = -1)
{
int count = 0;
// 异步遍历 KeysAsync
await foreach (var key in server.KeysAsync(database: database, pattern: pattern, pageSize: pageSize))
{
count++;
}
return count;
}
}
}