性能优化实现
⚡ MediatR 内部的性能优化机制和技巧
📖 概述
MediatR 在设计上已经非常轻量,但理解其内部的性能优化机制有助于更好地使用和扩展。本章分析:
- 表达式树编译 vs 反射
- 请求处理器工厂缓存
- 委托链优化
- 内存分配优化
🔬 表达式树编译 vs 反射
传统反射方式
csharp
// ❌ 慢:每次使用反射
var handlerType = typeof(CreateOrderHandler);
var handler = Activator.CreateInstance(handlerType, dependencies);
var method = handlerType.GetMethod("Handle");
var result = method.Invoke(handler, new object[] { request, cancellationToken });性能问题:
Activator.CreateInstance:~500nsGetMethod:~200nsInvoke:~300ns- 总计:~1000ns/次
表达式树编译方式
csharp
// ✅ 快:编译为委托
var handlerType = typeof(CreateOrderHandler);
// 构建表达式树
var param1 = Expression.Parameter(typeof(object[]), "args");
var param2 = Expression.Parameter(typeof(CancellationToken), "ct");
var newExpr = Expression.New(handlerType.GetConstructor(new[] { typeof(IDependency) }));
var convertExpr = Expression.Convert(newExpr, typeof(IRequestHandler<,>));
// 编译为委托
var factory = Expression.Lambda<Func<object[], CancellationToken, IRequestHandler>>(
convertExpr, param1, param2).Compile();
// 使用时直接调用委托
var handler = factory(args, cancellationToken); // ~10ns性能提升:
- 首次编译:~5000ns(一次性开销)
- 后续调用:~10ns
- 提升:100倍+
MediatR 的实现
MediatR 12.x:主要使用反射
MediatR 14+:引入源生成器,编译时生成代码,零反射
csharp
// MediatR 14+ 生成的代码(简化版)
public static class MediatRGeneratedCode
{
public static Task<TResponse> Send<TRequest, TResponse>(
IMediator mediator,
TRequest request,
CancellationToken ct) where TRequest : IRequest<TResponse>
{
// 直接调用,无反射
var wrapper = new RequestHandlerWrapperImpl<TRequest, TResponse>();
return wrapper.Handle(request, ServiceFactory, ct);
}
}💾 请求处理器工厂缓存
问题:每次都创建包装器
csharp
// MediatR 12.x 当前实现
public Task<TResponse> Send<TResponse>(IRequest<TResponse> request, CancellationToken ct)
{
// 每次都使用反射创建包装器
var wrapper = (RequestHandlerWrapper<TResponse>)Activator.CreateInstance(
typeof(RequestHandlerWrapperImpl<,>).MakeGenericType(
request.GetType(),
typeof(TResponse)));
return wrapper.Handle(request, _serviceFactory, ct);
}性能开销:
Activator.CreateInstance:~500nsMakeGenericType:~200ns- 每次请求:~700ns 额外开销
优化方案:缓存包装器
csharp
public class CachedMediator : Mediator
{
private static ConcurrentDictionary<Type, object> _wrapperCache = new();
public Task<TResponse> Send<TResponse>(IRequest<TResponse> request, CancellationToken ct)
{
var requestType = request.GetType();
var cacheKey = (requestType, typeof(TResponse));
// 从缓存获取包装器
var wrapper = (RequestHandlerWrapper<TResponse>)_wrapperCache.GetOrAdd(
cacheKey,
_ => Activator.CreateInstance(
typeof(RequestHandlerWrapperImpl<,>).MakeGenericType(requestType, typeof(TResponse))));
return wrapper.Handle(request, _serviceFactory, ct);
}
}性能提升:
- 首次请求:~700ns(创建+缓存)
- 后续请求:~10ns(直接从缓存获取)
- 提升:70倍
🔗 委托链优化
管道行为链的构建
csharp
// 当前实现:每次请求都重新构建
private RequestHandlerDelegate<TResponse> BuildPipeline(ServiceFactory serviceFactory)
{
RequestHandlerDelegate<TResponse> next = () =>
((IRequestHandler<TRequest, TResponse>)serviceFactory(typeof(IRequestHandler<TRequest, TResponse>)))
.Handle((TRequest)default, default);
var behaviors = (IEnumerable<IPipelineBehavior<TRequest, TResponse>>)
serviceFactory(typeof(IEnumerable<IPipelineBehavior<TRequest, TResponse>>));
foreach (var behavior in behaviors.Reverse())
{
var currentBehavior = behavior;
var nextDelegate = next;
next = async (request, ct) =>
await currentBehavior.Handle(request, () => nextDelegate(request, ct), ct);
}
return next;
}问题:
- 每次请求都遍历所有 Behavior
- 每次都创建新的委托链
- GC 压力大
优化:缓存委托链
csharp
public class OptimizedMediator : Mediator
{
private static ConcurrentDictionary<Type, Delegate> _pipelineCache = new();
private RequestHandlerDelegate<TResponse> GetCachedPipeline<TRequest, TResponse>(
ServiceFactory serviceFactory) where TRequest : IRequest<TResponse>
{
var key = typeof(TRequest);
return (RequestHandlerDelegate<TResponse>)_pipelineCache.GetOrAdd(key, _ =>
{
// 只构建一次
return BuildPipeline<TRequest, TResponse>(serviceFactory);
});
}
}效果:
- 减少委托创建:90%+
- 减少 GC 压力:60%+
- 响应时间更稳定
🧹 内存分配优化
避免闭包捕获
csharp
// ❌ 错误:产生闭包,增加内存分配
public class BadBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse>
{
public async Task<TResponse> Handle(TRequest request, RequestHandlerDelegate<TResponse> next, CancellationToken ct)
{
var requestId = Guid.NewGuid();
// 捕获局部变量,产生闭包
return await next(() =>
{
Console.WriteLine($"Request: {requestId}"); // ← 闭包
return next();
});
}
}内存分配:每次请求额外分配 ~50 字节(闭包对象)
csharp
// ✅ 正确:避免闭包
public class GoodBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse>
{
private readonly ILogger<GoodBehavior<TRequest, TResponse>> _logger;
public GoodBehavior(ILogger<GoodBehavior<TRequest, TResponse>> logger)
{
_logger = logger; // 注入依赖,不捕获局部变量
}
public async Task<TResponse> Handle(TRequest request, RequestHandlerDelegate<TResponse> next, CancellationToken ct)
{
_logger.LogInformation("Processing request"); // 无闭包
return await next();
}
}内存分配:零额外分配
使用 ValueTask 减少分配
csharp
// MediatR 14+ 支持 ValueTask
public interface IRequestHandler<in TRequest, TResponse>
where TRequest : IRequest<TResponse>
{
ValueTask<TResponse> Handle(TRequest request, CancellationToken cancellationToken);
}
// 对于同步或简单异步操作,使用 ValueTask
public class SimpleHandler : IRequestHandler<SimpleQuery, Result>
{
public ValueTask<Result> Handle(SimpleQuery request, CancellationToken ct)
{
// 无异步操作,直接返回
return new ValueTask<Result>(new Result { Data = "cached" });
}
}优势:
- 无异步操作时:零分配
- 有异步操作时:与 Task 相同
- 平均减少:30-50% 内存分配
🚀 源生成器优化(MediatR 14+)
传统方式的问题
csharp
// 运行时反射
var handlerType = assembly.GetTypes()
.FirstOrDefault(t => t.GetInterfaces().Any(i =>
i.IsGenericType &&
i.GetGenericTypeDefinition() == typeof(IRequestHandler<,>)));
var handler = Activator.CreateInstance(handlerType);问题:
- 启动时扫描所有类型
- 运行时反射创建实例
- 无编译时类型检查
源生成器方式
bash
dotnet add package MediatR.SourceGeneratorcsharp
// 编译时生成代码
[MediatRGenerated]
public partial class MediatRGeneratedCode
{
public static void RegisterMediatR(IServiceCollection services)
{
// 直接注册,无反射
services.AddTransient<IRequestHandler<CreateOrderCommand, OrderResult>, CreateOrderHandler>();
services.AddTransient<IRequestHandler<GetOrderQuery, OrderDto>, GetOrderHandler>();
services.AddTransient<IRequestHandler<UpdateUserCommand>, UpdateUserHandler>();
// ... 更多 Handler
}
}优势:
- ✅ 零反射开销
- ✅ 启动时间减少 80-90%
- ✅ 编译时类型检查
- ✅ IDE 智能提示
性能对比
| 指标 | 反射方式 | 源生成器 | 提升 |
|---|---|---|---|
| 启动时间(500 Handlers) | 150ms | 20ms | 87% |
| 首次请求延迟 | 125ns | 115ns | 8% |
| 内存占用 | 8MB | 5MB | 37% |
| 类型安全 | ❌ 运行时 | ✅ 编译时 | - |
🎯 最佳实践总结
性能优化清单
启动优化
- [ ] 只扫描必要的程序集
- [ ] 使用源生成器(MediatR 14+)
- [ ] 避免在 Startup 中执行耗时操作
- [ ] 考虑延迟初始化
运行时优化
- [ ] 使用 Scoped 生命周期(Web应用)
- [ ] 避免同步阻塞操作
- [ ] 使用异步 API
- [ ] 合理缓存查询结果
- [ ] 批量处理请求
内存优化
- [ ] 避免闭包捕获
- [ ] 使用 ValueTask(如适用)
- [ ] 使用对象池(高频场景)
- [ ] 减少管道行为数量
监控和优化
- [ ] 启用性能监控
- [ ] 定期运行基准测试
- [ ] 识别慢查询
- [ ] 分析内存分配
📊 性能调优案例
案例 1:电商系统优化
问题:订单创建接口响应时间 200ms
排查:
- 启用详细日志
- 发现验证行为耗时 80ms
- 数据库查询耗时 100ms
优化:
csharp
// 1. 异步验证
public class ValidationBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse>
{
public async Task<TResponse> Handle(TRequest request, RequestHandlerDelegate<TResponse> next, CancellationToken ct)
{
// 并行验证多个验证器
var validationTasks = _validators.Select(v => v.ValidateAsync(request, ct));
var results = await Task.WhenAll(validationTasks);
// ...
}
}
// 2. 缓存查询结果
public class CachingBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse>
{
public async Task<TResponse> Handle(TRequest request, RequestHandlerDelegate<TResponse> next, CancellationToken ct)
{
if (typeof(TRequest).Name.EndsWith("Query"))
{
var cacheKey = GenerateCacheKey(request);
if (_cache.TryGetValue(cacheKey, out TResponse cached))
return cached;
}
var response = await next(ct);
_cache.Set(cacheKey, response, TimeSpan.FromMinutes(5));
return response;
}
}结果:响应时间降至 50ms(提升 75%)
🎓 总结
关键优化点
- ✅ 使用源生成器:消除反射,提升启动性能
- ✅ 缓存包装器:减少重复创建
- ✅ 避免闭包:减少内存分配
- ✅ 使用 ValueTask:降低异步开销
- ✅ 合理缓存:减少重复计算
性能特征
| 场景 | 优化前 | 优化后 | 提升 |
|---|---|---|---|
| 启动时间 | 150ms | 20ms | 87% |
| 单次请求 | 125ns | 115ns | 8% |
| 内存分配 | 128B | 90B | 30% |
| 高并发TPS | 5000/s | 8000/s | 60% |
💡 提示: 先测量,再优化!使用 BenchmarkDotNet 确认瓶颈所在。