使用Semaphore在Middleware中处理单请求时请求丢失的问题排查
问题分析与修复方案
你的代码存在几个关键问题,导致后续请求被丢弃:
直接存储HttpContext是致命错误
ASP.NET Core的HttpContext是和当前请求绑定的临时对象,请求结束后框架会回收复用它。你把它入队后,等后续处理时,这个上下文已经失效,里面的请求信息、响应对象都不能正常工作了。队列处理逻辑混乱
你在finally块里递归调用InvokeAsync,这会导致上下文处理流程被打乱,而且后续入队的请求没有可靠的触发机制——如果没有新的请求进来触发InvokeAsync,队列里的请求就永远不会被处理。WaitAsync(TimeSpan.Zero)的使用方式不对
这个调用会立即返回,后续请求直接入队就返回,但此时没有主动去唤醒队列处理,只有当锁释放后的递归才会尝试,但这个逻辑不可靠。
修正后的代码
我们改用存储请求处理委托,而不是HttpContext,同时用后台循环来确保队列里的请求被依次处理:
using Microsoft.AspNetCore.Http; using System.Collections.Concurrent; public class OneRequestAtATimeMiddleware { private readonly RequestDelegate _next; private readonly SemaphoreSlim _semaphore = new SemaphoreSlim(1, 1); private readonly ConcurrentQueue<Func<Task>> _requestQueue = new ConcurrentQueue<Func<Task>>(); private bool _isProcessingQueue; public OneRequestAtATimeMiddleware(RequestDelegate next) { _next = next; } public async Task InvokeAsync(HttpContext context) { var endpoint = context.GetEndpoint(); if (endpoint?.Metadata?.GetMetadata<OneRequestAtATime>() != null) { // 将当前请求的处理逻辑包装成委托入队 var requestTask = new Func<Task>(async () => await _next(context)); _requestQueue.Enqueue(requestTask); // 尝试启动队列处理,避免重复启动 await TryProcessQueueAsync(); } else { await _next(context); } } private async Task TryProcessQueueAsync() { // 用双重检查锁定,确保只有一个线程在处理队列 if (_isProcessingQueue) return; try { await _semaphore.WaitAsync(); if (_isProcessingQueue) return; _isProcessingQueue = true; } finally { _semaphore.Release(); } try { // 循环处理队列里的所有请求 while (_requestQueue.TryDequeue(out var requestTask)) { await requestTask(); } } finally { _isProcessingQueue = false; } } [AttributeUsage(AttributeTargets.Method, AllowMultiple = false)] public class OneRequestAtATime : Attribute { } }
关键改动说明
- 存储请求处理委托:不再直接保存HttpContext,而是把
_next(context)包装成Func<Task>入队,这样每个请求的处理逻辑都是独立且有效的。 - 独立的队列处理方法:
TryProcessQueueAsync负责启动队列处理循环,用双重检查锁定确保同一时间只有一个线程在处理队列,避免重复执行。 - 可靠的触发机制:每个请求入队后都会尝试启动队列处理,不管当前锁是否可用,确保队列里的请求不会被遗漏。
这样就能保证标注了[OneRequestAtATime]的Action方法,同一时间只有一个请求在处理,其余请求会排队依次执行。
内容的提问来源于stack exchange,提问作者Alamzaib Farooq
相关产品推荐
相关产品推荐

