313 lines
11 KiB
C#
313 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>
|
|
/// <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)
|
|
{
|
|
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 = 100)
|
|
{
|
|
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 = 100, 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;
|
|
}
|
|
|
|
|
|
}
|
|
}
|