ASP.NET Core 中间件(Middleware)(一)

摘要:
基于此,我编写了一个中间件实践Demo记录请求输出以了解中间件。实体:publicclassRequestResponseLog{publicstringId{get;set;}publicDateTimeCreateTime{get;set;}publicstringRequestJson{get;set;}publicstringResponseJson{get;set;}}publicclassStudent{publicstringId{get;set;}publicstringName{get;set;}/////school////publicstringSchool{get;set;}////class////公共cstringClass{get;set;}//////Grade///publicstringGrade{get;set;}}控制器:用于接收请求[Route][ApiController]publicclassStudentController:控制器{[HttpGet]publicIActionResultGetStudent(){variable=newStudent(Id=Guid.NewGuid().ToString(),Class=“321”,Grade=“23”,Name=“Name001”,School=“School002”};returnOk;}}中间件:publicclassRequestResponseLoggingMiddleware{privateRequestDelegate_next;publicRequestResponsLoggingMiddle ware{this._next=next;}//////////˂/////publicasyncTaskInvoke{//First,gettheimingrequestvarrequest=awaitFormatRequest;varbody=context.Response.Body;//CopyapointertooriginalresponsebodystreamvaroriginalBodyStream=context.Respress.Body;//Createanewmemorystream…使用{//…并将其用于临时响应主体上下文。Response.Body=responseBody;//在中间件管道中继续,最终返回到该类await_next;//格式化来自服务器的响应;Response=awaitFormatResponse;//TODO:将日志保存到缓存或数据存储,暂时使用DemoQueueBlock<RequestResponseLog>。Add;//将新内存流的内容复制到原始流,然后返回发送给客户。awaitresponseBody。CopyToAsync;}}为了防止对实时存储数据库造成过大的压力,存储部分使用BlockingCollection实现的简单队列。

本文主要目标:记录Middleware的运行原理流程,并绘制流程图。

目录结构:

1、运行环境
2、Demo实践
3、源码追踪
4、AspnetCore内置middleware

ASP.NET Core 中间件(Middleware)(一)第1张

一、运行环境

Visual Studio Community 2019 版本 16.8.5

.Net Sdk Version: 5.0.103

二、Demo实践

讲解或学习一个东西的时候,最方便的方式是先写一个Demo。基于此,我写以一个中间件的记录请求输出的实践Demo来理解Middleware。

实体:

    public class RequestResponseLog
    {
        public string Id { get; set; }
        public DateTime CreateTime { get; set; }
        public string RequestJson { get; set; }
        public string ResponseJson { get; set; }
    }    
    
    public class Student
    {
        public string Id { get; set; }
        public string Name { get; set; }
        /// <summary>
        /// 学校
        /// </summary>
        public string School { get; set; }
        /// <summary>
        /// 班级
        /// </summary>
        public string Class { get; set; }
        /// <summary>
        /// 年级
        /// </summary>
        public string Grade { get; set; }
    }

Controller:用于接收请求

    [Route("api/[controller]")]
    [ApiController]
    public class StudentController : Controller
    {
        [HttpGet("GetStudent")]
        public IActionResult GetStudent()
        {
            var student = new Student()
            {
                Id = Guid.NewGuid().ToString(),
                Class = "321",
                Grade = "23",
                Name = "Name001",
                School = "School002"
            };
            return Ok(student);
        }
    }

Middleware 中间件(记录Request和Response):

public class RequestResponseLoggingMiddleware
    {
        private RequestDelegate _next;
        public RequestResponseLoggingMiddleware(RequestDelegate next)
        {
            this._next = next;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="httpContext"></param>
        /// <returns></returns>
        public async Task Invoke(HttpContext context)
        {

            //First, get the incoming request
            var request = await FormatRequest(context.Request);

            var body = context.Response.Body;

            //Copy a pointer to the original response body stream
            var originalBodyStream = context.Response.Body;

            //Create a new memory stream...
            using (var responseBody = new MemoryStream())
            {
                //...and use that for the temporary response body
                context.Response.Body = responseBody;

                //Continue down the Middleware pipeline, eventually returning to this class
                await _next(context);

                //Format the response from the server
                var response = await FormatResponse(context.Response);

                //TODO: Save log to chosen datastore,临时使用
                DemoQueueBlock<RequestResponseLog>.Add(new RequestResponseLog()
                {
                    Id=Guid.NewGuid().ToString(),
                    CreateTime = DateTime.Now,
                    ResponseJson = response,
                    RequestJson = request
                });

                //Copy the contents of the new memory stream (which contains the response) to the original stream, which is then returned to the client.
                await responseBody.CopyToAsync(originalBodyStream);
            }
        }

为了防止实时存储数据库压力过大,仓储部分用了BlockingCollection实现的简易队列。

blockingcollection-1.getconsumingenumerable

https://docs.microsoft.com/zh-cn/dotnet/api/system.collections.concurrent.blockingcollection-1.getconsumingenumerable?view=net-5.0

 public static void Consume(Action<T> func)
{
            Task.Factory.StartNew(() =>
            {
                foreach (var item in Colls.GetConsumingEnumerable())
                {
                    func(item);
                    Console.WriteLine(string.Format("---------------: {0}", item));
                }
            });
}

消费队列时入库:

public class DemoConsume
    {
        private readonly MysqlDbContext _dbContext;
        public DemoConsume(MysqlDbContext dbContext)
        {
            _dbContext = dbContext;
        }
        public bool Consume()
        {
            DemoQueueBlock<RequestResponseLog>.Consume(async (log)=> {
               await _dbContext.AddAsync(log);
               await _dbContext.SaveChangesAsync();
            });
            return true;
        }
    }

StartUp文件AddConsume和

app.UseMiddleware();

   public void ConfigureServices(IServiceCollection services)
        {
            services.AddControllers();
            var connection = Configuration.GetConnectionString("MysqlConnection");
            services.AddDbContext<MysqlDbContext>(options => options.UseMySQL(connection),ServiceLifetime.Scoped);
            services.AddConsume();
        }
        
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
           app.UseHttpsRedirection();
            app.UseRouting();
            app.UseAuthorization();
            app.UseMiddleware<RequestResponseLoggingMiddleware>();
            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllers();
            });
        }

Sql语句:

CREATE TABLE `request_response_log`  (
  `id` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `create_time` datetime(0) NULL DEFAULT NULL,
  `request_json` text CHARACTER SET utf8 COLLATE utf8_general_ci NULL,
  `response_json` text CHARACTER SET utf8 COLLATE utf8_general_ci NULL
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

运行程序效果:

ASP.NET Core 中间件(Middleware)(一)第2张

ASP.NET Core 中间件(Middleware)(一)第3张

可以看到该Demo提供了一个记录Http请求和输出日志的功能。

这里面和Middleware有关的功能为:

1、定义了RequestResponseLoggingMiddleware类

RequestDelegate向下转发请求,

Invoke方法

2、StartUp的app.UseMiddleware()。

这些方法具体怎么流转运行的呢?我们来搜一下源码可以确认下。

三、源码跟踪

所以我们可以看下UseMiddlewareExtensions

 public static class UseMiddlewareExtensions
    {
        internal const string InvokeMethodName = "Invoke";
        internal const string InvokeAsyncMethodName = "InvokeAsync";
 /// <summary>
        /// Adds a middleware type to the application's request pipeline.
        /// </summary>
        /// <param name="app">The <see cref="IApplicationBuilder"/> instance.</param>
        /// <param name="middleware">The middleware type.</param>
        /// <param name="args">The arguments to pass to the middleware type instance's constructor.</param>
        /// <returns>The <see cref="IApplicationBuilder"/> instance.</returns>
        public static IApplicationBuilder UseMiddleware(this IApplicationBuilder app, [DynamicallyAccessedMembers(MiddlewareAccessibility)] Type middleware, params object?[] args)
        {
            if (typeof(IMiddleware).IsAssignableFrom(middleware))
            {
                // IMiddleware doesn't support passing args directly since it's
                // activated from the container
                if (args.Length > 0)
                {
                    throw new NotSupportedException(Resources.FormatException_UseMiddlewareExplicitArgumentsNotSupported(typeof(IMiddleware)));
                }

                return UseMiddlewareInterface(app, middleware);
            }

            var applicationServices = app.ApplicationServices;
            return app.Use(next =>
            {
                var methods = middleware.GetMethods(BindingFlags.Instance | BindingFlags.Public);
                var invokeMethods = methods.Where(m =>
                    string.Equals(m.Name, InvokeMethodName, StringComparison.Ordinal)
                    || string.Equals(m.Name, InvokeAsyncMethodName, StringComparison.Ordinal)
                    ).ToArray();

                if (invokeMethods.Length > 1)
                {
                    throw new InvalidOperationException(Resources.FormatException_UseMiddleMutlipleInvokes(InvokeMethodName, InvokeAsyncMethodName));
                }

                if (invokeMethods.Length == 0)
                {
                    throw new InvalidOperationException(Resources.FormatException_UseMiddlewareNoInvokeMethod(InvokeMethodName, InvokeAsyncMethodName, middleware));
                }

                var methodInfo = invokeMethods[0];
                if (!typeof(Task).IsAssignableFrom(methodInfo.ReturnType))
                {
                    throw new InvalidOperationException(Resources.FormatException_UseMiddlewareNonTaskReturnType(InvokeMethodName, InvokeAsyncMethodName, nameof(Task)));
                }

                var parameters = methodInfo.GetParameters();
                if (parameters.Length == 0 || parameters[0].ParameterType != typeof(HttpContext))
                {
                    throw new InvalidOperationException(Resources.FormatException_UseMiddlewareNoParameters(InvokeMethodName, InvokeAsyncMethodName, nameof(HttpContext)));
                }

                var ctorArgs = new object[args.Length + 1];
                ctorArgs[0] = next;
                Array.Copy(args, 0, ctorArgs, 1, args.Length);
                var instance = ActivatorUtilities.CreateInstance(app.ApplicationServices, middleware, ctorArgs);
                if (parameters.Length == 1)
                {
                    return (RequestDelegate)methodInfo.CreateDelegate(typeof(RequestDelegate), instance);
                }

                var factory = Compile<object>(methodInfo, parameters);

                return context =>
                {
                    var serviceProvider = context.RequestServices ?? applicationServices;
                    if (serviceProvider == null)
                    {
                        throw new InvalidOperationException(Resources.FormatException_UseMiddlewareIServiceProviderNotAvailable(nameof(IServiceProvider)));
                    }

                    return factory(instance, context, serviceProvider);
                };
            });
        }

这里面用了

UseMiddleware(),进而调用

UseMiddleware(type TMiddleware)

进行了如下判断:

1、如果TMiddleware是继承了IMiddleware,则执行UseMiddlewareInterface方法。利用IMiddlewareFactory提供中间件的工厂创建方式,Microsoft.AspNetCore.Http提供了IMiddlewareFactory的默认实现MiddlewareFactory。

return app.Use(next =>
            {
                return async context =>
                {
                    var middlewareFactory = (IMiddlewareFactory?)context.RequestServices.GetService(typeof(IMiddlewareFactory));
                    if (middlewareFactory == null)
                    {
                        // No middleware factory
                        throw new InvalidOperationException(Resources.FormatException_UseMiddlewareNoMiddlewareFactory(typeof(IMiddlewareFactory)));
                    }

                    var middleware = middlewareFactory.Create(middlewareType);
                    if (middleware == null)
                    {
                        // The factory returned null, it's a broken implementation
                        throw new InvalidOperationException(Resources.FormatException_UseMiddlewareUnableToCreateMiddleware(middlewareFactory.GetType(), middlewareType));
                    }

                    try
                    {
                        await middleware.InvokeAsync(context, next);
                    }
                    finally
                    {
                        middlewareFactory.Release(middleware);
                    }
                };
            });

2、如果没有继承Middleware,则执行以下操作:

1、根据Invoke或InvokeAsync查找方法

2、验证只存在一个方法

3、验证返回类型为Task

4、验证第一个参数必须是HttpContext

5、ActivatorUtilities.CreateInstance 创建实例

6、如果只有一个参数,返回一个RequestDelegate类型的方法委托?

7、多个参数继续执行如下操作。Compile方法和参数。

var factory = Compile(methodInfo, parameters);
return context =>
{
var serviceProvider = context.RequestServices ?? applicationServices;
if (serviceProvider == null)
{
throw new InvalidOperationException(Resources.FormatException_UseMiddlewareIServiceProviderNotAvailable(nameof(IServiceProvider)));
}

                return factory(instance, context, serviceProvider);
            };

8、Compile演示了Lamuda表达式的编译方式,以后可作参考

 private static Func<T, HttpContext, IServiceProvider, Task> Compile<T>(MethodInfo methodInfo, ParameterInfo[] parameters)
        {
            // If we call something like
            //
            // public class Middleware
            // {
            //    public Task Invoke(HttpContext context, ILoggerFactory loggerFactory)
            //    {
            //
            //    }
            // }
            //

            // We'll end up with something like this:
            //   Generic version:
            //
            //   Task Invoke(Middleware instance, HttpContext httpContext, IServiceProvider provider)
            //   {
            //      return instance.Invoke(httpContext, (ILoggerFactory)UseMiddlewareExtensions.GetService(provider, typeof(ILoggerFactory));
            //   }

            //   Non generic version:
            //
            //   Task Invoke(object instance, HttpContext httpContext, IServiceProvider provider)
            //   {
            //      return ((Middleware)instance).Invoke(httpContext, (ILoggerFactory)UseMiddlewareExtensions.GetService(provider, typeof(ILoggerFactory));
            //   }

            var middleware = typeof(T);

            var httpContextArg = Expression.Parameter(typeof(HttpContext), "httpContext");
            var providerArg = Expression.Parameter(typeof(IServiceProvider), "serviceProvider");
            var instanceArg = Expression.Parameter(middleware, "middleware");

            var methodArguments = new Expression[parameters.Length];
            methodArguments[0] = httpContextArg;
            for (int i = 1; i < parameters.Length; i++)
            {
                var parameterType = parameters[i].ParameterType;
                if (parameterType.IsByRef)
                {
                    throw new NotSupportedException(Resources.FormatException_InvokeDoesNotSupportRefOrOutParams(InvokeMethodName));
                }

                var parameterTypeExpression = new Expression[]
                {
                    providerArg,
                    Expression.Constant(parameterType, typeof(Type)),
                    Expression.Constant(methodInfo.DeclaringType, typeof(Type))
                };

                var getServiceCall = Expression.Call(GetServiceInfo, parameterTypeExpression);
                methodArguments[i] = Expression.Convert(getServiceCall, parameterType);
            }

            Expression middlewareInstanceArg = instanceArg;
            if (methodInfo.DeclaringType != null && methodInfo.DeclaringType != typeof(T))
            {
                middlewareInstanceArg = Expression.Convert(middlewareInstanceArg, methodInfo.DeclaringType);
            }

            var body = Expression.Call(middlewareInstanceArg, methodInfo, methodArguments);

            var lambda = Expression.Lambda<Func<T, HttpContext, IServiceProvider, Task>>(body, instanceArg, httpContextArg, providerArg);

            return lambda.Compile();
        }

从上面我们可以看到这个扩展方法主要做了两件事:

判断是IMiddleware,然后采用不同的处理方式。

文章刚开始我们已经实践了非继承的模式,下面我们来实践下继承IMiddleware的模式。

public class TestMiddleware : IMiddleware
    {
        public async  Task InvokeAsync(HttpContext context, RequestDelegate next)
        {
            Console.WriteLine("TestMiddleware");
          await  next(context);
           // throw new NotImplementedException();
        }
    }

StartUp

(由于

MiddlewareFactory通过_serviceProvider.GetRequiredService(middlewareType) as IMiddleware获取中间件,所以需要在ConfigureServices里面注入TestMiddleware,不然会报错):

  public void ConfigureServices(IServiceCollection services)
        {
   services.AddSingleton<TestMiddleware>();
        }
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
         app.UseMiddleware<TestMiddleware>();
        }

效果如下:

ASP.NET Core 中间件(Middleware)(一)第4张

以上搜查暂时告一段落。

但里面还有个IApplicationBuilder的use方式尚没有看到使用方式,还需要继续探查。

IApplicationBuilder接口:

定义一个用于配置应用程序请求管道的类

public interface IApplicationBuilder
    {
        /// <summary>
        /// Gets or sets the <see cref="IServiceProvider"/> that provides access to the application's service container.
        /// </summary>
        IServiceProvider ApplicationServices { get; set; }

        /// <summary>
        /// Gets the set of HTTP features the application's server provides.
        /// </summary>
        IFeatureCollection ServerFeatures { get; }

        /// <summary>
        /// Gets a key/value collection that can be used to share data between middleware.
        /// </summary>
        IDictionary<string, object?> Properties { get; }

        /// <summary>
        /// Adds a middleware delegate to the application's request pipeline.
        /// </summary>
        /// <param name="middleware">The middleware delegate.</param>
        /// <returns>The <see cref="IApplicationBuilder"/>.</returns>
        IApplicationBuilder Use(Func<RequestDelegate, RequestDelegate> middleware);

        /// <summary>
        /// Creates a new <see cref="IApplicationBuilder"/> that shares the <see cref="Properties"/> of this
        /// <see cref="IApplicationBuilder"/>.
        /// </summary>
        /// <returns>The new <see cref="IApplicationBuilder"/>.</returns>
        IApplicationBuilder New();

        /// <summary>
        /// Builds the delegate used by this application to process HTTP requests.
        /// </summary>
        /// <returns>The request handling delegate.</returns>
        RequestDelegate Build();
    }

通过查看引用,我们可以看到提供了以下扩展:AspNetCore.Http.AbstractionsExtension

图片

通过翻看源码,可以看出这些扩展都是调用的IApplicationBuilder的use,我们只需要继续关注这个Use就行了。通过继续追溯源码,可以搜到IApplicationBuilderFactory的默认实现ApplicationBuilderFactory,它是一个创建ApplicationBuilder的工厂类。

 public class ApplicationBuilderFactory : IApplicationBuilderFactory
    {
        private readonly IServiceProvider _serviceProvider;

        /// <summary>
        /// Initialize a new factory instance with an <see cref="IServiceProvider" />.
        /// </summary>
        /// <param name="serviceProvider">The <see cref="IServiceProvider"/> used to resolve dependencies and initialize components.</param>
        public ApplicationBuilderFactory(IServiceProvider serviceProvider)
        {
            _serviceProvider = serviceProvider;
        }

        /// <summary>
        /// Create an <see cref="IApplicationBuilder" /> builder given a <paramref name="serverFeatures" />.
        /// </summary>
        /// <param name="serverFeatures">An <see cref="IFeatureCollection"/> of HTTP features.</param>
        /// <returns>An <see cref="IApplicationBuilder"/> configured with <paramref name="serverFeatures"/>.</returns>
        public IApplicationBuilder CreateBuilder(IFeatureCollection serverFeatures)
        {
            return new ApplicationBuilder(_serviceProvider, serverFeatures);
        }
    }

关注一下 ApplicationBuilder的重点部分:

 public class ApplicationBuilder : IApplicationBuilder
    {
         private const string ServerFeaturesKey = "server.Features";
        private const string ApplicationServicesKey = "application.Services";

        private readonly List<Func<RequestDelegate, RequestDelegate>> _components = new();

        /// <summary>
        /// Initializes a new instance of <see cref="ApplicationBuilder"/>.
        /// </summary>
        /// <param name="serviceProvider">The <see cref="IServiceProvider"/> for application services.</param>
        public ApplicationBuilder(IServiceProvider serviceProvider)
        {
            Properties = new Dictionary<string, object?>(StringComparer.Ordinal);
            ApplicationServices = serviceProvider;
        }

        /// <summary>
        /// Initializes a new instance of <see cref="ApplicationBuilder"/>.
        /// </summary>
        /// <param name="serviceProvider">The <see cref="IServiceProvider"/> for application services.</param>
        /// <param name="server">The server instance that hosts the application.</param>
        public ApplicationBuilder(IServiceProvider serviceProvider, object server)
            : this(serviceProvider)
        {
            SetProperty(ServerFeaturesKey, server);
        }

        private ApplicationBuilder(ApplicationBuilder builder)
        {
            Properties = new CopyOnWriteDictionary<string, object?>(builder.Properties, StringComparer.Ordinal);
        }

        /// <summary>
        /// Gets the <see cref="IServiceProvider"/> for application services.
        /// </summary>
        public IServiceProvider ApplicationServices
        {
            get
            {
                return GetProperty<IServiceProvider>(ApplicationServicesKey)!;
            }
            set
            {
                SetProperty<IServiceProvider>(ApplicationServicesKey, value);
            }
        }

        /// <summary>
        /// Gets the <see cref="IFeatureCollection"/> for server features.
        /// </summary>
        public IFeatureCollection ServerFeatures
        {
            get
            {
                return GetProperty<IFeatureCollection>(ServerFeaturesKey)!;
            }
        }

        /// <summary>
        /// Gets a set of properties for <see cref="ApplicationBuilder"/>.
        /// </summary>
        public IDictionary<string, object?> Properties { get; }

        private T? GetProperty<T>(string key)
        {
            return Properties.TryGetValue(key, out var value) ? (T?)value : default(T);
        }

        private void SetProperty<T>(string key, T value)
        {
            Properties[key] = value;
        }

        /// <summary>
        /// Adds the middleware to the application request pipeline.
        /// </summary>
        /// <param name="middleware">The middleware.</param>
        /// <returns>An instance of <see cref="IApplicationBuilder"/> after the operation has completed.</returns>
        public IApplicationBuilder Use(Func<RequestDelegate, RequestDelegate> middleware)
        {
            _components.Add(middleware);
            return this;
        }   
/// <summary>
        /// Produces a <see cref="RequestDelegate"/> that executes added middlewares.
        /// </summary>
        /// <returns>The <see cref="RequestDelegate"/>.</returns>
        public RequestDelegate Build()
        {
            RequestDelegate app = context =>
            {
                // If we reach the end of the pipeline, but we have an endpoint, then something unexpected has happened.
                // This could happen if user code sets an endpoint, but they forgot to add the UseEndpoint middleware.
                var endpoint = context.GetEndpoint();
                var endpointRequestDelegate = endpoint?.RequestDelegate;
                if (endpointRequestDelegate != null)
                {
                    var message =
                        $"The request reached the end of the pipeline without executing the endpoint: '{endpoint!.DisplayName}'. " +
                        $"Please register the EndpointMiddleware using '{nameof(IApplicationBuilder)}.UseEndpoints(...)' if using " +
                        $"routing.";
                    throw new InvalidOperationException(message);
                }

                context.Response.StatusCode = StatusCodes.Status404NotFound;
                return Task.CompletedTask;
            };

            for (var c = _components.Count - 1; c >= 0; c--)
            {
                app = _components[c](app);
            }

            return app;
        }
}

从上面源码的实现来看Use的作用仅仅是将一个中间件添加到List<Func<RequestDelegate, RequestDelegate>> _components里面,换句话来讲就是将一个RequestDelegate的委托放到一个list里面。

流程图如下:

图片

四、Asp.netCore内置Middleware举例:

以ConcurrencyLimiterMiddleware为例,传入的请求进行排队处理,避免线程池的不足.

 public class ConcurrencyLimiterMiddleware
    {
        private readonly IQueuePolicy _queuePolicy;
        private readonly RequestDelegate _next;
        private readonly RequestDelegate _onRejected;
        private readonly ILogger _logger;

        /// <summary>
        /// Creates a new <see cref="ConcurrencyLimiterMiddleware"/>.
        /// </summary>
        /// <param name="next">The <see cref="RequestDelegate"/> representing the next middleware in the pipeline.</param>
        /// <param name="loggerFactory">The <see cref="ILoggerFactory"/> used for logging.</param>
        /// <param name="queue">The queueing strategy to use for the server.</param>
        /// <param name="options">The options for the middleware, currently containing the 'OnRejected' callback.</param>
        public ConcurrencyLimiterMiddleware(RequestDelegate next, ILoggerFactory loggerFactory, IQueuePolicy queue, IOptions<ConcurrencyLimiterOptions> options)
        {
            if (options.Value.OnRejected == null)
            {
                throw new ArgumentException("The value of 'options.OnRejected' must not be null.", nameof(options));
            }

            _next = next;
            _logger = loggerFactory.CreateLogger<ConcurrencyLimiterMiddleware>();
            _onRejected = options.Value.OnRejected;
            _queuePolicy = queue;
        }

        /// <summary>
        /// Invokes the logic of the middleware.
        /// </summary>
        /// <param name="context">The <see cref="HttpContext"/>.</param>
        /// <returns>A <see cref="Task"/> that completes when the request leaves.</returns>
        public async Task Invoke(HttpContext context)
        {
            var waitInQueueTask = _queuePolicy.TryEnterAsync();

            // Make sure we only ever call GetResult once on the TryEnterAsync ValueTask b/c it resets.
            bool result;

            if (waitInQueueTask.IsCompleted)
            {
                ConcurrencyLimiterEventSource.Log.QueueSkipped();
                result = waitInQueueTask.Result;
            }
            else
            {
                using (ConcurrencyLimiterEventSource.Log.QueueTimer())
                {
                    result = await waitInQueueTask;
                }
            }

            if (result)
            {
                try
                {
                    await _next(context);
                }
                finally
                {
                    _queuePolicy.OnExit();
                }
            }
            else
            {
                ConcurrencyLimiterEventSource.Log.RequestRejected();
                ConcurrencyLimiterLog.RequestRejectedQueueFull(_logger);
                context.Response.StatusCode = StatusCodes.Status503ServiceUnavailable;
                await _onRejected(context);
            }
        }

需要注意的是有两个:

1、IQueuePolicy,asp.netCore内置了两种实现QueuePolicy和StackPolicy,这里就不贴代码了,主要是关于堆和栈的实现逻辑。

2、ConcurrencyLimiterOptions

QueuePolicyServiceCollectionExtensions

 public static class QueuePolicyServiceCollectionExtensions
    {
        /// <summary>
        /// Tells <see cref="ConcurrencyLimiterMiddleware"/> to use a FIFO queue as its queueing strategy.
        /// </summary>
        /// <param name="services">The <see cref="IServiceCollection"/> to add services to.</param>
        /// <param name="configure">Set the options used by the queue.
        /// Mandatory, since <see cref="QueuePolicyOptions.MaxConcurrentRequests"></see> must be provided.</param>
        /// <returns></returns>
        public static IServiceCollection AddQueuePolicy(this IServiceCollection services, Action<QueuePolicyOptions> configure)
        {
            services.Configure(configure);
            services.AddSingleton<IQueuePolicy, QueuePolicy>();
            return services;
        }

        /// <summary>
        /// Tells <see cref="ConcurrencyLimiterMiddleware"/> to use a LIFO stack as its queueing strategy.
        /// </summary>
        /// <param name="services">The <see cref="IServiceCollection"/> to add services to.</param>
        /// <param name="configure">Set the options used by the queue.
        /// Mandatory, since <see cref="QueuePolicyOptions.MaxConcurrentRequests"></see> must be provided.</param>
        /// <returns></returns>
        public static IServiceCollection AddStackPolicy(this IServiceCollection services, Action<QueuePolicyOptions> configure)
        {
            services.Configure(configure);
            services.AddSingleton<IQueuePolicy, StackPolicy>();
            return services;
        }
    }


 public class QueuePolicyOptions
    {
        /// <summary>
        /// Maximum number of concurrent requests. Any extras will be queued on the server. 
        /// This option is highly application dependant, and must be configured by the application.
        /// </summary>
        public int MaxConcurrentRequests { get; set; }

        /// <summary>
        /// Maximum number of queued requests before the server starts rejecting connections with '503 Service Unavailable'.
        /// This option is highly application dependant, and must be configured by the application.
        /// </summary>
        public int RequestQueueLimit { get; set; }
    }

通过源码可以大概看出使用方式了吧,这里就不做实践了。

今天的分享到此结束,谢谢观看。
由于排版问题,原文请参考:https://mp.weixin.qq.com/s/nm8Pa-q3oOInX0LIw9swNA

免责声明:文章转载自《ASP.NET Core 中间件(Middleware)(一)》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇EasyNVR推流平台实现海康大华宇视网络摄像头内容网页无插件播放的方法介绍O/R Mapping 工具介绍 LLBLgen控件(转)下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

相关文章

2-开发共享版APP(接入指南)-设备接入说明:快速接入

https://www.cnblogs.com/yangfengwu/p/11249674.html 该APP安装包下载链接: http://www.mnif.cn/appapk/IotDevelopmentVersion/20190820/app-debug.apk 或者扫描二维码下载 APP源码获取方式:(请阅读宝贝说明) 此APP建立在基础篇,升...

inno setup读取注册表遇到的一个坑

一、背景       目前,公司针对PR开发的一个插件需要发布到64位系统上。该插件包括一个prm格式的文件和若干个DLL文件。其中,prm文件需要复制到PR公共插件目录下,DLL需要复制到Windows系统目录中去,这样插件才能正常的工作。公司现在要求发布插件时制作一个安装包,让用户点击安装包后自动将插件相关文件拷贝到相应目录去。本来用inno setu...

Quartz的基本使用之入门(2.3.0版本)

一、Quartz可以用来做什么 Quartz是一个强大任务调度框架,我工作时候会在这些情况下使用到quartz框架,当然还有很多的应用场景,在这里只列举2个实际用到的 餐厅系统会在每周四晚上的22点自动审核并生成报表 人事系统会在每天早晨8点给有待办的人员自动发送Email提醒 二、使用Quartz之前的准备 1.建立一个Maven项目 2.引入qu...

动态加载与插件系统的初步实现(3):WinForm示例

动态加载与插件系统的初步实现(三):WinForm示例 代码文件在此Download,本文章围绕前文所述默认AppDomain、插件容器AppDomain两个域及IPlugin、PluginProvider、PluginProxy3个类的使用与变化进行。 添加WinForm项目Host、类库Plugin、引用System.Windows.Forms;的类...

react-native获取屏幕尺寸

项目中需要获取手机的尺寸 import {Dimensions} from "react-native" var WINDOW = Dimensions.get("window"); var width = WINDOW.width; var height = WINDOW.height; var scale = WINDOW.scale; 其中 widt...

EXCEL导出,列表宽度大于内容列

//TIPS:列宽设置在最后 public static string Export<T>(string rootPath, List<T> list, string fileName, IDictionary<string, string> header, Action<int, string, string,...