Skip to content

Commit e76ba51

Browse files
committed
perf: 优化调用异步终结点性能
2 parents 1e7c5bd + 5496751 commit e76ba51

File tree

3 files changed

+79
-37
lines changed

3 files changed

+79
-37
lines changed

Cyaim.WebSocketServer/Cyaim.WebSocketServer/Infrastructure/Configures/WatchAssemblyContext.cs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
1-
using System;
1+
using System;
22
using System.Collections.Concurrent;
33
using System.Collections.Generic;
44
using System.Reflection;
5+
using System.Threading.Tasks;
56

67
namespace Cyaim.WebSocketServer.Infrastructure.Configures
78
{
@@ -52,5 +53,11 @@ public class WatchAssemblyContext
5253
/// K MethodInfo,V ParameterInfo
5354
/// </summary>
5455
public Dictionary<MethodInfo, ParameterInfo[]> MethodParameters { get; set; }
56+
57+
/// <summary>
58+
/// Task result getter cache in endpoint method
59+
/// K endpoint MethodInfo,V Task result getter
60+
/// </summary>
61+
public Dictionary<MethodInfo, Func<Task, object>> MethodTaskResultGetters { get; set; }
5562
}
5663
}

Cyaim.WebSocketServer/Cyaim.WebSocketServer/Infrastructure/Handlers/MvcHandler/MvcChannelHandler.cs

Lines changed: 19 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
using Cyaim.WebSocketServer.Infrastructure.AccessControl;
1+
using Cyaim.WebSocketServer.Infrastructure.AccessControl;
22
using Cyaim.WebSocketServer.Infrastructure.Configures;
33
using Cyaim.WebSocketServer.Infrastructure.Injectors;
44
using Cyaim.WebSocketServer.Infrastructure.Metrics;
@@ -1076,53 +1076,33 @@ public static async Task<MvcResponseScheme> MvcDistributeAsync(WebSocketRouteOpt
10761076
// Async api support
10771077
if (invokeResult is Task task)
10781078
{
1079-
var taskType = task.GetType();
1080-
bool isGenericTask = taskType.IsGenericType && taskType.GetGenericTypeDefinition() == typeof(Task<>);
1079+
await Task.WhenAny(task, Task.Delay(Timeout.Infinite, appLifetime.ApplicationStopping));
10811080

1082-
// 预先准备好反射所需的 PropertyInfo(如果是 Task<T>)
1083-
PropertyInfo resultProperty = null;
1084-
if (isGenericTask) resultProperty = taskType.GetProperty("Result");
1085-
1086-
try
1087-
{
1088-
// 等待任务完成(异步操作,不阻塞线程)
1089-
await task.ConfigureAwait(false);
1090-
}
1091-
catch (Exception ex)
1092-
{
1093-
// await 会抛出 AggregateException,提取内部异常
1094-
if (ex is AggregateException aggEx && aggEx.InnerException != null)
1095-
{
1096-
ex = aggEx.InnerException;
1097-
}
1098-
mvcResponse = await webSocketOptions.OnException(ex, request, mvcResponse, context, webSocketOptions, context.Request.Path, logger).ConfigureAwait(false);
1099-
}
1100-
1101-
// 检查任务状态(await 后任务已完成,但需要检查是否有异常或取消)
1102-
if (task.IsFaulted && task.Exception != null)
1081+
if (task.IsCanceled || task.IsFaulted)
11031082
{
1104-
// 抛出内部异常(AggregateException 的第一个内部异常)
1105-
var innerException = task.Exception.InnerException ?? task.Exception;
1106-
mvcResponse = await webSocketOptions.OnException(innerException, request, mvcResponse, context, webSocketOptions, context.Request.Path, logger).ConfigureAwait(false);
1083+
await task;
11071084
}
11081085

1109-
if (task.IsCanceled)
1086+
if (task.Exception != null)
11101087
{
1111-
mvcResponse = await webSocketOptions.OnException(new TaskCanceledException(task), request, mvcResponse, context, webSocketOptions, context.Request.Path, logger).ConfigureAwait(false);
1088+
throw task.Exception;
11121089
}
11131090

1114-
// 检查是否是 Task<T> 类型,需要获取返回值
1115-
if (isGenericTask && resultProperty != null)
1091+
if (method.ReturnType == typeof(Task))
11161092
{
1117-
// 使用预先准备好的 PropertyInfo 获取结果(此时任务已完成,不会阻塞)
1118-
invokeResult = resultProperty.GetValue(task);
1093+
invokeResult = null;
11191094
}
11201095
else
11211096
{
1122-
invokeResult = null;
1097+
Func<Task, object> taskResultGetter = null;
1098+
webSocketOptions.WatchAssemblyContext.MethodTaskResultGetters?.TryGetValue(method, out taskResultGetter);
1099+
invokeResult = taskResultGetter != null
1100+
? taskResultGetter(task)
1101+
: null;
11231102
}
11241103
}
11251104

1105+
11261106
#endregion
11271107

11281108

@@ -1137,7 +1117,11 @@ public static async Task<MvcResponseScheme> MvcDistributeAsync(WebSocketRouteOpt
11371117
{
11381118
MvcResponseScheme resp = new MvcResponseScheme() { Id = request.Id, Status = 1, Target = request.Target, RequestTime = requestTime, CompleteTime = DateTime.Now.Ticks };
11391119

1140-
ex = (ex.InnerException ?? ex);
1120+
if (ex is AggregateException aggEx && aggEx.InnerException != null)
1121+
{
1122+
ex = aggEx.InnerException;
1123+
}
1124+
11411125
resp.Msg = string.Format(I18nText.WS_INTERACTIVE_TEXT_TEMPALTE, context.Connection.RemoteIpAddress, context.Connection.RemotePort, context.Connection.Id, I18nText.MvcDistributeAsync_Target + requestPath + Environment.NewLine + ex.Message + Environment.NewLine + ex.StackTrace);
11421126
logger.LogInformation(resp.Msg);
11431127

Cyaim.WebSocketServer/Cyaim.WebSocketServer/Infrastructure/WebSocketRouteServiceCollectionExtensions.cs

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
using Cyaim.WebSocketServer.Infrastructure.Attributes;
1+
using Cyaim.WebSocketServer.Infrastructure.Attributes;
22
using Cyaim.WebSocketServer.Infrastructure.Configures;
33
using Microsoft.Extensions.Configuration;
44
using Microsoft.Extensions.DependencyInjection;
@@ -10,7 +10,9 @@
1010
using System.Collections.Generic;
1111
using System.Globalization;
1212
using System.Linq;
13+
using System.Linq.Expressions;
1314
using System.Reflection;
15+
using System.Threading.Tasks;
1416

1517
namespace Cyaim.WebSocketServer.Infrastructure
1618
{
@@ -182,6 +184,20 @@ public static void ConfigureWebSocketRoute(this IServiceCollection services, ICo
182184

183185
#endregion
184186

187+
#region 计算终结点返回值Task<TResult>结果读取器缓存
188+
189+
var methodTaskResultGetters = new Dictionary<MethodInfo, Func<Task, object>>();
190+
foreach (var item in points.Select(x => x.MethodInfo))
191+
{
192+
if (TryBuildTaskResultGetter(item.ReturnType, out var taskResultGetter))
193+
{
194+
methodTaskResultGetters[item] = taskResultGetter;
195+
}
196+
}
197+
wsrOptions.WatchAssemblyContext.MethodTaskResultGetters = methodTaskResultGetters;
198+
199+
#endregion
200+
185201
services.AddSingleton(x => wsrOptions);
186202

187203
//services.TryAddSingleton<IHttpContextAccessor, HttpContextAccessor>();
@@ -194,6 +210,41 @@ public static void ConfigureWebSocketRoute(this IServiceCollection services, ICo
194210
var defaultCultureInfo = CultureInfo.CurrentCulture;
195211
}
196212

213+
/// <summary>
214+
/// 编译表达式树以获取 Task<TResult> 的结果,避免每次通过反射访问 Result 属性的性能开销。
215+
/// </summary>
216+
/// <param name="returnType"></param>
217+
/// <param name="getter"></param>
218+
/// <returns></returns>
219+
private static bool TryBuildTaskResultGetter(Type returnType, out Func<Task, object> getter)
220+
{
221+
getter = null;
222+
if (returnType == null || !returnType.IsGenericType)
223+
{
224+
return false;
225+
}
226+
227+
var genericTypeDef = returnType.GetGenericTypeDefinition();
228+
if (genericTypeDef != typeof(Task<>))
229+
{
230+
return false;
231+
}
232+
233+
var resultType = returnType.GenericTypeArguments[0];
234+
if (resultType.Name == "VoidTaskResult" && resultType.Namespace == "System.Threading.Tasks")
235+
{
236+
return false;
237+
}
238+
239+
var taskParam = Expression.Parameter(typeof(Task), "task");
240+
var castTask = Expression.Convert(taskParam, returnType);
241+
var resultProperty = Expression.Property(castTask, "Result");
242+
var castResult = Expression.Convert(resultProperty, typeof(object));
243+
244+
getter = Expression.Lambda<Func<Task, object>>(castResult, taskParam).Compile();
245+
return true;
246+
}
247+
197248
/// <summary>
198249
/// Get assembly controller "WebSocket" EndPoint
199250
/// </summary>

0 commit comments

Comments
 (0)