Просмотр исходного кода

1、优化接口压测的功能方法

jian.tao 1 год назад
Родитель
Сommit
9ef0228b63

+ 14 - 0
Admin.NET/Admin.NET.Core/Extension/ObjectExtension.cs

@@ -4,6 +4,8 @@
 //
 // 不得利用本项目从事危害国家安全、扰乱社会秩序、侵犯他人合法权益等法律法规禁止的活动!任何基于本项目二次开发而产生的一切法律纠纷和责任,我们不承担任何责任!
 
+using System.Text.Json;
+
 namespace Admin.NET.Core;
 
 /// <summary>
@@ -446,4 +448,16 @@ public static partial class ObjectExtension
             }
         }
     }
+
+    /// <summary>
+    /// 深复制
+    /// </summary>
+    /// <typeparam name="T">深复制源对象</typeparam>
+    /// <param name="obj">对象</param>
+    /// <returns></returns>
+    public static T DeepCopy<T>(this T obj)
+    {
+        var json = JsonSerializer.Serialize(obj);
+        return JsonSerializer.Deserialize<T>(json);
+    }
 }

+ 76 - 51
Admin.NET/Admin.NET.Core/Service/Common/SysCommonService.cs

@@ -121,12 +121,39 @@ public class SysCommonService : IDynamicApiController, ITransient
         if (!_userManager.SuperAdmin) throw Oops.Oh(ErrorCodeEnum.SA001);
 
         var stopwatch = new Stopwatch();
-        var responseTimes = new List<double>();
+        var responseTimes = new List<double>();  //响应时间集合
+        input.RequestMethod = input.RequestMethod.ToUpper();
         long totalRequests = 0, successfulRequests = 0, failedRequests = 0;
 
         stopwatch.Start();
         var semaphore = new SemaphoreSlim(input.MaxDegreeOfParallelism!.Value > 0 ? input.MaxDegreeOfParallelism.Value : Environment.ProcessorCount);
 
+        #region 参数构建
+
+        // 构建基础URI(不包括路径和查询参数)
+        var baseUriBuilder = new UriBuilder(input.RequestUri);
+        var queryString = HttpUtility.ParseQueryString(baseUriBuilder.Query);
+
+        // 替换路径参数到baseUriBuilder.Path
+        foreach (var param in input.PathParameters)
+        {
+            baseUriBuilder.Path = baseUriBuilder.Path.Replace($"{{{param.Key}}}", param.Value, StringComparison.OrdinalIgnoreCase);
+        }
+
+        // 构建Query参数
+        foreach (var param in input.QueryParameters)
+        {
+            queryString[param.Key] = param.Value;
+        }
+
+        baseUriBuilder.Query = queryString.ToString() ?? string.Empty;
+        var fullUri = baseUriBuilder.Uri;
+
+        // 创建一次性的HttpRequestMessage模板
+        HttpRequestMessage requestTemplate = CreateRequestMessage(input, fullUri);
+
+        #endregion
+
         var tasks = Enumerable.Range(0, input.NumberOfRounds!.Value * input.NumberOfRequests!.Value).Select(async _ =>
         {
             await semaphore.WaitAsync();
@@ -135,50 +162,23 @@ public class SysCommonService : IDynamicApiController, ITransient
                 var requestStopwatch = new Stopwatch();
                 requestStopwatch.Start();
 
-                // 构建完整的请求URI,包括路径参数和查询参数
-                var uriBuilder = new UriBuilder(input.RequestUri);
-                var queryString = HttpUtility.ParseQueryString(uriBuilder.Query);
-
-                foreach (var param in input.PathParameters)
-                {
-                    uriBuilder.Path = uriBuilder.Path.Replace($"{{{param.Key}}}", param.Value, StringComparison.OrdinalIgnoreCase);
-                }
-
-                foreach (var param in input.QueryParameters)
+                using (var request = requestTemplate.DeepCopy())
                 {
-                    queryString[param.Key] = param.Value;
-                }
-
-                uriBuilder.Query = queryString.ToString() ?? string.Empty;
-                var fullUri = uriBuilder.Uri.ToString();
+                    if (!string.Equals(input.RequestMethod, "GET", StringComparison.OrdinalIgnoreCase) && input.RequestParameters.Any())
+                    {
+                        var content = new FormUrlEncodedContent(input.RequestParameters);
+                        request.Content = content;
+                    }
 
-                HttpRequestMessage request = input.RequestMethod.ToUpper() switch
-                {
-                    "GET" => new HttpRequestMessage(HttpMethod.Get, fullUri),
-                    "PUT" => new HttpRequestMessage(HttpMethod.Put, fullUri),
-                    "POST" => new HttpRequestMessage(HttpMethod.Post, fullUri),
-                    "DELETE" => new HttpRequestMessage(HttpMethod.Delete, fullUri),
-                    _ => throw Oops.Bah("请求方式异常")
-                };
-
-                // 设置请求头
-                foreach (var header in input.Headers)
-                {
-                    request.Headers.Add(header.Key, header.Value);
-                }
+                    using (var response = await _httpClient.SendAsync(request))
+                    {
+                        response.EnsureSuccessStatusCode(); // 抛出错误状态码异常
 
-                if (nameof(HttpMethod.Get).EqualIgnoreCase(input.RequestMethod) && input.RequestParameters.Any())
-                {
-                    var content = new FormUrlEncodedContent(input.RequestParameters);
-                    request.Content = content;
+                        requestStopwatch.Stop();
+                        responseTimes.Add(requestStopwatch.Elapsed.TotalMilliseconds);
+                        Interlocked.Increment(ref successfulRequests);
+                    }
                 }
-
-                var response = await _httpClient.SendAsync(request);
-                response.EnsureSuccessStatusCode(); // 抛出错误状态码异常
-
-                requestStopwatch.Stop();
-                responseTimes.Add(requestStopwatch.Elapsed.TotalMilliseconds);
-                Interlocked.Increment(ref successfulRequests);
             }
             catch
             {
@@ -196,6 +196,7 @@ public class SysCommonService : IDynamicApiController, ITransient
 
         var totalTimeInSeconds = stopwatch.Elapsed.TotalSeconds;
         var qps = totalTimeInSeconds > 0 ? totalRequests / totalTimeInSeconds : 0;
+        var orderResponseTimes = responseTimes.OrderBy(t => t).ToList();
         var averageResponseTime = responseTimes.Any() ? responseTimes.Average() : 0;
         var minResponseTime = responseTimes.Any() ? responseTimes.Min() : 0;
         var maxResponseTime = responseTimes.Any() ? responseTimes.Max() : 0;
@@ -210,16 +211,41 @@ public class SysCommonService : IDynamicApiController, ITransient
             MinResponseTime = minResponseTime,
             MaxResponseTime = maxResponseTime,
             AverageResponseTime = averageResponseTime,
-            Percentile10ResponseTime = CalculatePercentile(responseTimes, 0.1),
-            Percentile25ResponseTime = CalculatePercentile(responseTimes, 0.25),
-            Percentile50ResponseTime = CalculatePercentile(responseTimes, 0.5),
-            Percentile75ResponseTime = CalculatePercentile(responseTimes, 0.75),
-            Percentile90ResponseTime = CalculatePercentile(responseTimes, 0.9),
-            Percentile99ResponseTime = CalculatePercentile(responseTimes, 0.99),
-            Percentile999ResponseTime = CalculatePercentile(responseTimes, 0.999)
+            Percentile10ResponseTime = CalculatePercentile(orderResponseTimes, 0.1),
+            Percentile25ResponseTime = CalculatePercentile(orderResponseTimes, 0.25),
+            Percentile50ResponseTime = CalculatePercentile(orderResponseTimes, 0.5),
+            Percentile75ResponseTime = CalculatePercentile(orderResponseTimes, 0.75),
+            Percentile90ResponseTime = CalculatePercentile(orderResponseTimes, 0.9),
+            Percentile99ResponseTime = CalculatePercentile(orderResponseTimes, 0.99),
+            Percentile999ResponseTime = CalculatePercentile(orderResponseTimes, 0.999)
         };
     }
 
+    /// <summary>
+    /// 创建请求消息
+    /// </summary>
+    /// <param name="input">输入参数</param>
+    /// <param name="fullUri">url</param>
+    /// <returns></returns>
+    private HttpRequestMessage CreateRequestMessage(StressTestInput input, Uri fullUri)
+    {
+        HttpRequestMessage request = input.RequestMethod switch
+        {
+            "GET" => new HttpRequestMessage(HttpMethod.Get, fullUri),
+            "PUT" => new HttpRequestMessage(HttpMethod.Put, fullUri),
+            "POST" => new HttpRequestMessage(HttpMethod.Post, fullUri),
+            "DELETE" => new HttpRequestMessage(HttpMethod.Delete, fullUri),
+            _ => throw Oops.Bah("请求方式异常")
+        };
+
+        // 设置请求头
+        foreach (var header in input.Headers)
+        {
+            request.Headers.Add(header.Key, header.Value);
+        }
+        return request;
+    }
+
     /// <summary>
     /// 计算百分位请求耗时
     /// </summary>
@@ -229,8 +255,7 @@ public class SysCommonService : IDynamicApiController, ITransient
     private double CalculatePercentile(List<double> times, double percentile)
     {
         if (!times.Any()) return 0;
-        var sortedTimes = times.OrderBy(t => t).ToList();
-        var index = (int)Math.Ceiling(percentile * sortedTimes.Count) - 1;
-        return sortedTimes[index < sortedTimes.Count ? index : sortedTimes.Count - 1];
+        var index = (int)Math.Ceiling(percentile * times.Count) - 1;
+        return times[index < times.Count ? index : times.Count - 1];
     }
 }