Skip to content

性能优化实现 ​

⚡ 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:~500ns
  • GetMethod:~200ns
  • Invoke:~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:~500ns
  • MakeGenericType:~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.SourceGenerator
csharp
// 编译时生成代码
[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)150ms20ms87%
首次请求延迟125ns115ns8%
内存占用8MB5MB37%
类型安全❌ 运行时✅ 编译时-

🎯 最佳实践总结 ​

性能优化清单 ​

启动优化 ​

  • [ ] 只扫描必要的程序集
  • [ ] 使用源生成器(MediatR 14+)
  • [ ] 避免在 Startup 中执行耗时操作
  • [ ] 考虑延迟初始化

运行时优化 ​

  • [ ] 使用 Scoped 生命周期(Web应用)
  • [ ] 避免同步阻塞操作
  • [ ] 使用异步 API
  • [ ] 合理缓存查询结果
  • [ ] 批量处理请求

内存优化 ​

  • [ ] 避免闭包捕获
  • [ ] 使用 ValueTask(如适用)
  • [ ] 使用对象池(高频场景)
  • [ ] 减少管道行为数量

监控和优化 ​

  • [ ] 启用性能监控
  • [ ] 定期运行基准测试
  • [ ] 识别慢查询
  • [ ] 分析内存分配

📊 性能调优案例 ​

案例 1:电商系统优化 ​

问题:订单创建接口响应时间 200ms

排查:

  1. 启用详细日志
  2. 发现验证行为耗时 80ms
  3. 数据库查询耗时 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%)


🎓 总结 ​

关键优化点 ​

  1. ✅ 使用源生成器:消除反射,提升启动性能
  2. ✅ 缓存包装器:减少重复创建
  3. ✅ 避免闭包:减少内存分配
  4. ✅ 使用 ValueTask:降低异步开销
  5. ✅ 合理缓存:减少重复计算

性能特征 ​

场景优化前优化后提升
启动时间150ms20ms87%
单次请求125ns115ns8%
内存分配128B90B30%
高并发TPS5000/s8000/s60%

💡 提示: 先测量,再优化!使用 BenchmarkDotNet 确认瓶颈所在。

Released under the CC BY-SA 4.0 License.