using molilian.core; using dodohold.core; using Microsoft.AspNetCore.Mvc; using System.Text; using System.Text.Json; using System.Collections.Concurrent; using System.Diagnostics; namespace molilian.api.Controllers { [ApiController] [Route("[controller]/[action]")] public class FeatureController : ControllerBase { protected IHttpContextAccessor _accessor; public FeatureController(IHttpContextAccessor accessor) { _accessor = accessor; } [HttpGet] public async Task CSRedis(int thread, int durationSeconds = 30) { var watch = System.Diagnostics.Stopwatch.StartNew(); var cts = new CancellationTokenSource(); var performanceLogger = new RedisPerformanceLogger(); long totalRequests = 0; long successCount = 0; long failureCount = 0; var metricsQueue = new ConcurrentQueue(); var tasks = new List(); for (int i = 0; i < thread; i++) { tasks.Add(Task.Run(() => { while (!cts.Token.IsCancellationRequested) { var metrics = new RedisOperationMetrics(); try { // 写入操作 var sw = Stopwatch.StartNew(); var testValue = "test value"; var key = $"test:csredis:{Guid.NewGuid()}"; RedisHelper.Set(key, testValue, 60); sw.Stop(); metrics.WriteTimeMs = sw.ElapsedMilliseconds; metrics.KeySize = Encoding.UTF8.GetByteCount(key); metrics.ValueSize = Encoding.UTF8.GetByteCount(testValue); // IncrBy操作 sw.Restart(); RedisHelper.IncrBy("counter:csredis", 1); sw.Stop(); metrics.IncrByTimeMs = sw.ElapsedMilliseconds; // 读取操作 sw.Restart(); var value = RedisHelper.Get("counter:csredis"); sw.Stop(); metrics.ReadTimeMs = sw.ElapsedMilliseconds; metrics.IsSuccess = true; Interlocked.Increment(ref successCount); } catch (Exception ex) { metrics.IsSuccess = false; metrics.ErrorMessage = ex.Message; Interlocked.Increment(ref failureCount); string message = $"{ex.Message}\n{ex.StackTrace}"; NotifyCore.Notify(new NifyMessage { message = message, priority = NifyMessagePriority.high, tags = ["red_circle"] }); new LoggerLibrary("Feature").Info(message).SaveAsync(); } finally { metricsQueue.Enqueue(metrics); Interlocked.Increment(ref totalRequests); } } }, cts.Token)); } await Task.Delay(TimeSpan.FromSeconds(durationSeconds)); cts.Cancel(); try { await Task.WhenAll(tasks); } catch (OperationCanceledException) { // 预期的取消异常 } watch.Stop(); var elapsedMs = watch.ElapsedMilliseconds; var qps = totalRequests * 1000.0 / elapsedMs; var metrics = metricsQueue.ToList(); var writeStats = RedisOperationMetrics.CalculateStatistics(metrics, m => m.WriteTimeMs); var incrByStats = RedisOperationMetrics.CalculateStatistics(metrics, m => m.IncrByTimeMs); var readStats = RedisOperationMetrics.CalculateStatistics(metrics, m => m.ReadTimeMs); var resultData = new Dictionary { ["ConcurrentThreads"] = thread, ["DurationSeconds"] = durationSeconds, ["TotalTimeMs"] = elapsedMs, ["TotalRequests"] = totalRequests, ["SuccessCount"] = successCount, ["FailureCount"] = failureCount, ["QPS"] = Math.Round(qps, 2), ["SuccessRate"] = Math.Round((double)successCount / totalRequests * 100, 2), ["RedisMetrics"] = new { Write = writeStats, IncrBy = incrByStats, Read = readStats } }; await performanceLogger.LogPerformanceResult("CSRedis", resultData); return new APIResult(resultData); } [HttpGet] public async Task FreeRedis(int thread, int durationSeconds = 30) { var watch = System.Diagnostics.Stopwatch.StartNew(); var cts = new CancellationTokenSource(); var performanceLogger = new RedisPerformanceLogger(); long totalRequests = 0; long successCount = 0; long failureCount = 0; var metricsQueue = new ConcurrentQueue(); var redis = YunhuiKit.RedisKit.Instance; var tasks = new List(); for (int i = 0; i < thread; i++) { tasks.Add(Task.Run(() => { while (!cts.Token.IsCancellationRequested) { var metrics = new RedisOperationMetrics(); try { // 写入操作 var sw = Stopwatch.StartNew(); var testValue = "test value"; var key = $"test:freeredis:{Guid.NewGuid()}"; redis.Set(key, testValue, 60); sw.Stop(); metrics.WriteTimeMs = sw.ElapsedMilliseconds; metrics.KeySize = Encoding.UTF8.GetByteCount(key); metrics.ValueSize = Encoding.UTF8.GetByteCount(testValue); // IncrBy操作 sw.Restart(); redis.IncrBy("counter:freeredis", 1); sw.Stop(); metrics.IncrByTimeMs = sw.ElapsedMilliseconds; // 读取操作 sw.Restart(); var value = redis.Get("counter:freeredis"); sw.Stop(); metrics.ReadTimeMs = sw.ElapsedMilliseconds; metrics.IsSuccess = true; Interlocked.Increment(ref successCount); } catch (Exception ex) { metrics.IsSuccess = false; metrics.ErrorMessage = ex.Message; Interlocked.Increment(ref failureCount); string message = $"{ex.Message}\n{ex.StackTrace}"; NotifyCore.Notify(new NifyMessage { message = message, priority = NifyMessagePriority.high, tags = ["red_circle"] }); new LoggerLibrary("Feature").Info(message).SaveAsync(); } finally { metricsQueue.Enqueue(metrics); Interlocked.Increment(ref totalRequests); } } }, cts.Token)); } await Task.Delay(TimeSpan.FromSeconds(durationSeconds)); cts.Cancel(); try { await Task.WhenAll(tasks); } catch (OperationCanceledException) { // 预期的取消异常 } watch.Stop(); var elapsedMs = watch.ElapsedMilliseconds; var qps = totalRequests * 1000.0 / elapsedMs; var metrics = metricsQueue.ToList(); var writeStats = RedisOperationMetrics.CalculateStatistics(metrics, m => m.WriteTimeMs); var incrByStats = RedisOperationMetrics.CalculateStatistics(metrics, m => m.IncrByTimeMs); var readStats = RedisOperationMetrics.CalculateStatistics(metrics, m => m.ReadTimeMs); var resultData = new Dictionary { ["ConcurrentThreads"] = thread, ["DurationSeconds"] = durationSeconds, ["TotalTimeMs"] = elapsedMs, ["TotalRequests"] = totalRequests, ["SuccessCount"] = successCount, ["FailureCount"] = failureCount, ["QPS"] = Math.Round(qps, 2), ["SuccessRate"] = Math.Round((double)successCount / totalRequests * 100, 2), ["RedisMetrics"] = new { Write = writeStats, IncrBy = incrByStats, Read = readStats } }; await performanceLogger.LogPerformanceResult("FreeRedis", resultData); return new APIResult(resultData); } } public class RedisPerformanceLogger { private readonly LoggerLibrary _logger; private const string LoggerName = "RedisPerformance"; public RedisPerformanceLogger() { _logger = new LoggerLibrary(LoggerName); } public async Task LogPerformanceResult(string clientType, Dictionary metrics) { var logMessage = new StringBuilder(); logMessage.AppendLine($"Redis Performance Test Result - {clientType}"); logMessage.AppendLine($"DateTime: {DateTime.Now:yyyy-MM-dd HH:mm:ss}"); foreach (var kvp in metrics) { logMessage.AppendLine($"{kvp.Key}: {JsonSerializer.Serialize(kvp.Value, new JsonSerializerOptions { WriteIndented = true })}"); } await _logger.Info(logMessage.ToString()).SaveAsync(); } } public class RedisOperationMetrics { public long WriteTimeMs { get; set; } public long IncrByTimeMs { get; set; } public long ReadTimeMs { get; set; } public DateTime OperationTime { get; set; } = DateTime.Now; public string OperationType { get; set; } public bool IsSuccess { get; set; } public string ErrorMessage { get; set; } public int RetryCount { get; set; } public long KeySize { get; set; } public long ValueSize { get; set; } public static double CalculatePercentile(List times, double percentile) { if (times == null || times.Count == 0) return 0; var sortedTimes = times.OrderBy(t => t).ToList(); var index = (int)Math.Ceiling(percentile / 100.0 * sortedTimes.Count) - 1; return sortedTimes[Math.Max(0, index)]; } public static OperationStatistics CalculateStatistics(IEnumerable metrics, Func timeSelector) { var times = metrics.Select(timeSelector).ToList(); return new OperationStatistics { Average = times.Any() ? Math.Round(times.Average(), 2) : 0, Median = CalculatePercentile(times, 50), P95 = CalculatePercentile(times, 95), P99 = CalculatePercentile(times, 99), Max = times.Any() ? times.Max() : 0, Min = times.Any() ? times.Min() : 0, Count = times.Count, TotalTime = times.Sum() }; } } public class OperationStatistics { public double Average { get; set; } public double Median { get; set; } public double P95 { get; set; } public double P99 { get; set; } public long Max { get; set; } public long Min { get; set; } public int Count { get; set; } public long TotalTime { get; set; } } }