.net core 中间件管道底层剖析

本文涉及的产品
Serverless 应用引擎免费试用套餐包,4320000 CU,有效期3个月
服务治理 MSE Sentinel/OpenSergo,Agent数量 不受限
云原生网关 MSE Higress,422元/月
简介: .net core 中间件管道底层剖析.net core 管道(Pipeline)是什么?由上图可以看出,.net core 管道是请求抵达服务器到响应结果返回的中间的一系列的处理过程,如果我们简化一下成下图来看的话,.net core 的管道其实就是中间件的部分。

.net core 中间件管道底层剖析
.net core 管道(Pipeline)是什么?

由上图可以看出,.net core 管道是请求抵达服务器到响应结果返回的中间的一系列的处理过程,如果我们简化一下成下图来看的话,.net core 的管道其实就是中间件的部分。微软中间件文档

为什么管道就是中间件的部分了呢?我是这么理解的,.net core 是通过Startup 类配置服务和应用的请求管道,所以狭义点来讲这个管道就是指的请求管道,就是我们今天要理解的中间件管道。

.net core 核心体系结构的特点就是一个中间件系统,它是处理请求和响应的代码段。中间件彼此链接,形成一个管道。传入的请求通过管道传递,其中每个中间件都有机会在将它们传递到下一个中间件之前对它们进行处理。传出响应也以相反的顺序通过管道传递。

PS:简单来讲就是请求开始到响应结束的中间的一大部分。你可以理解成 " 汽车销售 " (开始买车到提车的过程,但愿不会坐在奔驰车盖上哭),哈哈……

还有我们来看看,为什么我们要简化来看,在运行时 .net core 会预先注入一些必要的服务及依赖项,默认注入(ServiceCollection)的服务清单如下:

我们先断章取义地看,这里面有 Kestrel 处理请求,将接收到的请求内容(字符串流)转化成结构化的数据(HttpContext)供后面的中间件使用的服务。欸,服务哟。那其实也就是 Kestrel 服务也是中间件嘛。

而第一张图中的MVC本身也作为中间件来实现的。

还有一些相关的服务都可以看看上面截图的服务,而且旁边标注的生命周期的类型就是之前所讲到的 .net core 的三种注入模式 。

那么从程序入口来讲,过程是怎么样的呢?

从应用程序主入口 Main() --> WebHost --> UseStartup

复制代码
///
/// Specify the startup type to be used by the web host.
///
/// The to configure.
/// The to be used.
/// The .
public static IWebHostBuilder UseStartup(this IWebHostBuilder hostBuilder, Type startupType)
{

string name = startupType.GetTypeInfo().Assembly.GetName().Name;
return hostBuilder.UseSetting(WebHostDefaults.ApplicationKey, name).ConfigureServices((Action<IServiceCollection>)delegate(IServiceCollection services)
{
    if (typeof(IStartup).GetTypeInfo().IsAssignableFrom(startupType.GetTypeInfo()))
    {
        ServiceCollectionServiceExtensions.AddSingleton(services, typeof(IStartup), startupType);
    }
    else
    {
        ServiceCollectionServiceExtensions.AddSingleton(services, typeof(IStartup), (Func<IServiceProvider, object>)delegate(IServiceProvider sp)
        {
            IHostingEnvironment requiredService = ServiceProviderServiceExtensions.GetRequiredService<IHostingEnvironment>(sp);
            return new ConventionBasedStartup(StartupLoader.LoadMethods(sp, startupType, requiredService.get_EnvironmentName()));
        });
    }
});

}
复制代码
上面的代码就可以解释说,会预先注入的必要的服务,在通过委托的方式,注入 Startup 里的服务。具体可以继续探究:

UseSetting:Add or replace a setting in the configuration.
复制代码
///
/// Add or replace a setting in the configuration.
///
/// The key of the setting to add or replace.
/// The value of the setting to add or replace.
/// The .
public IWebHostBuilder UseSetting(string key, string value)
{

_config.set_Item(key, value);
return this;

}
复制代码
ConfigureServices:Adds a delegate for configuring additional services for the host or web application. This may be called multiple times.
复制代码
///
/// Adds a delegate for configuring additional services for the host or web application. This may be called
/// multiple times.
///
/// A delegate for configuring the .
/// The .
public IWebHostBuilder ConfigureServices(Action configureServices)
{

if (configureServices == null)
{
    throw new ArgumentNullException("configureServices");
}
return ConfigureServices(delegate(WebHostBuilderContext _, IServiceCollection services)
{
    configureServices(services);
});

}
复制代码
ConventionBasedStartup

复制代码
public class ConventionBasedStartup : IStartup
{

private readonly StartupMethods _methods;

public ConventionBasedStartup(StartupMethods methods)
{
    _methods = methods;
}

public void Configure(IApplicationBuilder app)
{
    try
    {
        _methods.ConfigureDelegate(app);
    }
    catch (Exception ex)
    {
        if (ex is TargetInvocationException)
        {
            ExceptionDispatchInfo.Capture(ex.InnerException).Throw();
        }
        throw;
    }
}

public IServiceProvider ConfigureServices(IServiceCollection services)
{
    try
    {
        return _methods.ConfigureServicesDelegate(services);
    }
    catch (Exception ex)
    {
        if (ex is TargetInvocationException)
        {
            ExceptionDispatchInfo.Capture(ex.InnerException).Throw();
        }
        throw;
    }
}

}
复制代码
OK,到这里就已经确定了,我们可控的是通过Startup注入我们所需的服务,就是Startup注入的中间件可以做所有的事情,如处理认证,错误,静态文件等等,并且如上面所说的 MVC 在 .net core 也是作为中间件实现的。

那么 .net core 给我们内置了多少中间件呢?如下图:

我们很经常用到的内置中间件有:

app.UseExceptionHandler(); //异常处理
app.UseStaticFiles(); //静态文件
app.UseAuthentication(); //Auth验证
app.UseMvc(); //MVC

我们知道可以在启动类的 Configure 方法中配置 .net core 管道,通过调用 IApplicationBuilder 上的 Use* 方法,就可以向管道添加一个中间件,被添加的顺序决定了请求遍历它们的顺序。因此,如上面添加内置中间件的顺序,传入的请求将首先遍历异常处理程序中间件,然后是静态文件中间件,然后是身份验证中间件,最终将由MVC中间件处理。

Use* 方法实际上只是 .net core 提供给我们的“快捷方式”,以便更容易地构建管道。在幕后,它们最终都使用(直接或间接)这些关键字:Use 和 Run 。两者都向管道中添加了一个中间件,不同之处在于Run添加了一个终端中间件,即管道中的最后一个中间件。

那么有内置,就应该可以定制的。如何定制自己的中间件呢?上一些简陋的demo演示一下:

(1)无分支管道

复制代码
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{

   // Middleware A
   app.Use(async (context, next) =>
   {
        Console.WriteLine("A (before)");
        await next();
        Console.WriteLine("A (after)");
   });

   // Middleware B
   app.Use(async (context, next) =>
   {
        Console.WriteLine("B (before)");
        await next();
        Console.WriteLine("B (after)");
   });

   // Middleware C (terminal)
   app.Run(async context =>
   {
        Console.WriteLine("C");
        await context.Response.WriteAsync("Hello world");
   });

}
复制代码

     打印结果:

A (before)
B (before)
C
B (after)
A (after)

     那用管道图展示的话就是:

(2)有分支管道,当使用无分支的管道时,相当于就是一条线直走到底再返回响应结果。但一般情况下,我们都希望管道更具灵活性。创建有分支的管道就需要使用到 Map 扩展用作约定来创建管道分支。Map 是基于给定请求路径的匹配项来创建请求管道分支的,如果请求路径以给定的路径开头,就执行分支。那么就有两种类型有分支的管道:

    1.无连结分支,上官方demo:

复制代码
public class Startup
{

private static void HandleMapTest1(IApplicationBuilder app)
{
    app.Run(async context =>
    {
        await context.Response.WriteAsync("Map Test 1");
    });
}

private static void HandleMapTest2(IApplicationBuilder app)
{
    app.Run(async context =>
    {
        await context.Response.WriteAsync("Map Test 2");
    });
}

public void Configure(IApplicationBuilder app)
{
    app.Map("/map1", HandleMapTest1);

    app.Map("/map2", HandleMapTest2);

    app.Run(async context =>
    {
        await context.Response.WriteAsync("Hello from non-Map delegate. <p>");
    });
}

}
复制代码

        结果:
       以上无连结分支很容易就理解了,就是不同的路径跑不同的分支。如果是有参数匹配的话,就要使用 MapWhen,而 MapWhen 基于给定谓词的结果创建请求管道分支。Func<HttpContext, bool> 类型的任何谓词均可用于将请求映射到管道的新分支。 谓词用于检测查询字符串变量 branch 是否存在。

    2.有连结(重新连接上主管道)分支,创建有连结分支管道就要使用到 UseWhen,上demo:

复制代码
public void Configure(IApplicationBuilder app)
{

app.Use(async (context, next) =>
{
    Console.WriteLine("A (before)");
    await next();
    Console.WriteLine("A (after)");
});

app.UseWhen(
    context => context.Request.Path.StartsWithSegments(new PathString("/foo")),
    a => a.Use(async (context, next) =>
    {
        Console.WriteLine("B (before)");
        await next();
        Console.WriteLine("B (after)");
    }));

app.Run(async context =>
{
    Console.WriteLine("C");
    await context.Response.WriteAsync("Hello world");
});

}
复制代码

        像上面的代码,当请求不是以 " /foo " 开头的时候,结果为:

A (before)
C
A (after)

      当请求是以 " /foo " 开头的时候,结果为:

A (before)
B (before)
C
B (after)
A (after)

     正如您所看到的,中间件管道背后的思想非常简单,但是非常强大。大多数功能都是 .net core(身份验证、静态文件、缓存、MVC等)作为中间件实现。当然,编写自己的代码也很容易!

     后面可以进阶写自己的中间件,官方文档。

原文地址https://www.cnblogs.com/Vam8023/p/10700254.html

相关文章
|
27天前
|
开发框架 .NET 开发者
简化 ASP.NET Core 依赖注入(DI)注册-Scrutor
Scrutor 是一个简化 ASP.NET Core 应用程序中依赖注入(DI)注册过程的开源库,支持自动扫描和注册服务。通过简单的配置,开发者可以轻松地从指定程序集中筛选、注册服务,并设置其生命周期,同时支持服务装饰等高级功能。适用于大型项目,提高代码的可维护性和简洁性。仓库地址:&lt;https://github.com/khellang/Scrutor&gt;
43 5
|
2月前
|
开发框架 .NET C#
在 ASP.NET Core 中创建 gRPC 客户端和服务器
本文介绍了如何使用 gRPC 框架搭建一个简单的“Hello World”示例。首先创建了一个名为 GrpcDemo 的解决方案,其中包含一个 gRPC 服务端项目 GrpcServer 和一个客户端项目 GrpcClient。服务端通过定义 `greeter.proto` 文件中的服务和消息类型,实现了一个简单的问候服务 `GreeterService`。客户端则通过 gRPC 客户端库连接到服务端并调用其 `SayHello` 方法,展示了 gRPC 在 C# 中的基本使用方法。
49 5
在 ASP.NET Core 中创建 gRPC 客户端和服务器
|
1月前
|
开发框架 缓存 .NET
GraphQL 与 ASP.NET Core 集成:从入门到精通
本文详细介绍了如何在ASP.NET Core中集成GraphQL,包括安装必要的NuGet包、创建GraphQL Schema、配置GraphQL服务等步骤。同时,文章还探讨了常见问题及其解决方法,如处理复杂查询、错误处理、性能优化和实现认证授权等,旨在帮助开发者构建灵活且高效的API。
28 3
|
12天前
|
开发框架 算法 中间件
ASP.NET Core 中的速率限制中间件
在ASP.NET Core中,速率限制中间件用于控制客户端请求速率,防止服务器过载并提高安全性。通过`AddRateLimiter`注册服务,并配置不同策略如固定窗口、滑动窗口、令牌桶和并发限制。这些策略可在全局、控制器或动作级别应用,支持自定义响应处理。使用中间件`UseRateLimiter`启用限流功能,并可通过属性禁用特定控制器或动作的限流。这有助于有效保护API免受滥用和过载。 欢迎关注我的公众号:Net分享 (239字符)
30 0
|
3月前
|
开发框架 JavaScript 前端开发
一个适用于 ASP.NET Core 的轻量级插件框架
一个适用于 ASP.NET Core 的轻量级插件框架
|
8月前
|
消息中间件 存储 负载均衡
消息中间件的选择:RabbitMQ是一个明智的选择
消息中间件的选择:RabbitMQ是一个明智的选择
122 0
|
7月前
|
消息中间件 存储 中间件
【消息中间件】详解三大MQ:RabbitMQ、RocketMQ、Kafka
【消息中间件】详解三大MQ:RabbitMQ、RocketMQ、Kafka
1888 0
|
6月前
|
消息中间件 编解码 Docker
Docker部署RabbitMQ消息中间件
【7月更文挑战第4天】Docker部署RabbitMQ消息中间件
288 3
|
3月前
|
消息中间件 编解码 Docker
【Docker项目实战】Docker部署RabbitMQ消息中间件
【10月更文挑战第8天】Docker部署RabbitMQ消息中间件
141 1
【Docker项目实战】Docker部署RabbitMQ消息中间件
|
5月前
|
消息中间件 Java 测试技术
消息中间件RabbitMQ---SpringBoot整合RabbitMQ【三】
这篇文章是关于如何在SpringBoot应用中整合RabbitMQ的消息中间件。内容包括了在SpringBoot项目中添加RabbitMQ的依赖、配置文件设置、启动类注解,以及如何通过单元测试来创建交换器、队列、绑定,并发送和接收消息。文章还介绍了如何配置消息转换器以支持对象的序列化和反序列化,以及如何使用注解`@RabbitListener`来接收消息。
消息中间件RabbitMQ---SpringBoot整合RabbitMQ【三】