How can I learn ASP. NET Core? How can I not understand the request processing pipeline [2]: The "Leader" position of the server in the pipeline? asp. netcore
The ASP. NET Core pipeline consists of registered servers and a series of middleware. We have thoroughly analyzed the middleware in the previous article. Now let's take a look at the server. The server is the first node in the ASP. NET Core pipeline. It monitors and receives complete requests, and is also responsible for the response to the requests. [This Article has been synchronized to ASP. NET Core framework secrets]
Server is a general term for all types of IServer interfaces and corresponding objects. As shown in the following code snippet, this interface has a read-only attribute Features that returns the FeatureCollection object describing its own feature set, and another Start method is used to Start the server.
1: public interface IServer : IDisposable
2: {
3: IFeatureCollection Features { get; }
4: void Start<TContext>(IHttpApplication<TContext> application);
5: }
When the Start method starts a specified Server, you must specify a parameter of the IHttpApplication type <TContext>. all types of the Implemented interfaces and their corresponding objects are collectively referred to as HttpApplication. When the server receives the incoming request, it will directly send it to the HttpApplication object for processing, so we need to first understand this object.
I. HttpApplication
For the ASP. NET Core pipeline, HttpApplication takes over the requests received by the server, and is solely responsible for subsequent requests. As shown in, after HttpApplication receives a request from the server, it uses the registered middleware registration to process the request and finally submits the request to the application. HttpApplication processes requests in an execution context, which defines a boundary for the application to process a single request. Simply describing the HTTP request HttpContext is the core part of the execution context. In addition, we can also define other related information as needed, therefore, the IHttpApplication <TContext> interface uses generic parameters to indicate the type of the context.
HttpApplication not only needs to process the requests forwarded by the server in this execution context, but also needs to create and release the context object. As shown in the following code snippet, The CreateContext and DisposeContext methods of the IHttpApplication <TContext> interface respectively reflect the creation and release of the execution context. The contextFeatures parameter of the CreateContext method represents a set of features describing the original context. In this context, request processing is implemented in another method ProcessRequestAsync.
1: public interface IHttpApplication<TContext>
2: {
3: TContext CreateContext(IFeatureCollection contextFeatures);
4: void DisposeContext(TContext context, Exception exception);
5: Task ProcessRequestAsync(TContext context);
6: }
By default, the created HttpApplication is a HostingApplication object. For HostingApplication, the type of the execution Context it creates is a structure Context with the following definitions. For the execution Context of the current request represented by this Context object, it is the most important part to describe the HttpContext of the current HTTP request. In addition to the HttpContext attribute, Context has two additional attributes. Scope is the log Context range created for tracking and diagnosis. This range is associated with multiple log records of the same request, another property, StartTimestamp, indicates the timestamp at which the application starts to process requests.
1: public class HostingApplication : IHttpApplication<Context>
2: {
3: // omit the Member
4: public struct Context
5: {
6: public HttpContext HttpContext { get; set; }
7: public IDisposable Scope { get; set; }
8: public long StartTimestamp { get; set; }
9: }
10: }
Because HostingApplication processes requests through the registered middleware, these middleware will eventually use the ApplicationBuilder object described above to convert to a principal object of the RequestDelegate type, all middleware processes requests by executing this delegate object. We need to provide such a RequestDelegate object when creating the HostingApplication. The Context object created by HostingApplication contains the HttpContext object that represents the HTTP Context, which is created through the corresponding factory HttpContextFactory. Therefore, HttpContextFactory must be provided during creation. As shown in the following code snippet, The HostingApplication constructor must take these two objects as input parameters. As for the other two parameters (logger and diagnosticSource), they are related to log records.
1: public class HostingApplication : IHttpApplication<HostingApplication.Context>
2: {
3: private readonly RequestDelegate _application;
4: private readonly DiagnosticSource _diagnosticSource;
5: private readonly IHttpContextFactory _httpContextFactory;
6: private readonly ILogger _logger;
7:
8: public HostingApplication(RequestDelegate application, ILogger logger, DiagnosticSource diagnosticSource, IHttpContextFactory httpContextFactory)
9: {
10: _application = application;
11: _logger = logger;
12: _diagnosticSource = diagnosticSource;
13: _httpContextFactory = httpContextFactory;
14: }
15: }
The code snippets given below basically reflect the logic for HostingApplication to create and release Context objects and process requests in this Context. In the CreateContext method, it directly uses the HttpContextFactory provided by initialization to create an HttpContext and use it as the attribute of the same name of the Context object. How can we set the additional two attributes of Context (Scope and StartTimestamp, we will introduce this in the subsequent sections. The process of requests in the ProcessRequestAsync method is ultimately reflected in the execution of the RequestDelegate object specified during the construction. When the DisposeContext method is executed, the Scope attribute of the Context is released first. Then, the Dispose method of HttpContextFactory is called to release the Context object.
1: public class HostingApplication : IHttpApplication<HostingApplication.Context>
2: {
3: public Context CreateContext(IFeatureCollection contextFeatures)
4: {
5: // omit other implementation code
6: return new Context
7: {
8: HttpContext = _httpContextFactory.Create(contextFeatures),
9: Scope = ...,
10: StartTimestamp = ...
11: };
12: }
13:
14: public Task ProcessRequestAsync(Context context)
15: {
16: Return _application(context.HttpContext);
17: }
18:
19: public void DisposeContext(Context context, Exception exception)
20: {
21: // omit other implementation code
22: context.Scope.Dispose();
23: _httpContextFactory.Dispose(context.HttpContext);
24: }
25: }
Ii. KestrelServer
Cross-platform is a notable feature of ASP. NET Core, while KestrelServer is the only server that Microsoft has launched to be truly cross-platform. The KestrelServer uses a network engine named KestrelEngine to listen, receive, and respond to requests. KetrelServer features cross-platform because KestrelEngine was developed on a cross-platform network library named libuv. When talking about libuv, we have to talk about libev, a network library for event loops and event models in Unix systems. Because of its high performance, libev has become the most popular network library after lievent and Event perl module. Because Libev does not support Windows, some people create an abstraction layer on libev to shield the differences between platforms. This abstraction layer is libuv. Libuv is implemented in the form of IOCP on Windows platform, revealing the cross-platform implementation principle of libuv for Unix and Windows. So far, the platforms supported by libuv are no longer limited to Unix and Windows, including Linux (2.6), MacOS and Solaris (121 and later versions) the platform is within the range supported by libuv.
The code snippets shown below reflect the definition of the KestrelServer type. In addition to the Features attribute defined by the IServer interface, KestrelServer also has a read-only attribute Options of the KestrelServerOptions type. This attribute indicates the settings related to the KestrelServer. when calling the constructor, We initialize this attribute using the IOptions <KestrelServerOptions> Object represented by the input parameter options. The constructor also has two additional parameters: IApplicationLifetime and ILoggerFactory. The latter is used to create the Logger that records logs. The former is related to application lifecycle management.
1: public class KestrelServer : IServer
2: {
3: public IFeatureCollection Features { get; }
4: public KestrelServerOptions Options { get; }
5:
6: public KestrelServer(IOptions<KestrelServerOptions> options, IApplicationLifetime applicationLifetime, ILoggerFactory loggerFactory);
7: public void Dispose();
8: public void Start<TContext>(IHttpApplication<TContext> application);
9: }
Registered KetrelServer will be created in the pipeline in the form of dependency injection, and the constructor injection is used to provide its constructor Parameter options. Because this parameter type is IOptions <KestrelServerOptions>, therefore, the Options model is used to specify the settings of the KestrelServerOptions object. For example, we can define the configuration of KestrelServer in the next json file.
1: {
2: "noDelay" : false,
3: "shutdownTimeout" : "00:00:10",
4: "threadCount" : 10
5: }
In order for the application to load such a configuration file (the file name is assumed to be "KestrelServerOptions. json), we only need to use ConfigurationBuilder to load the Configuration file and generate the corresponding Configuration object as follows, finally, follow the programming method of the Options model to map the KestrelServerOptions type to this object. Service Registration for KestrelServerOptions can also be defined in the ConfigureServices method of the Startup type.
1: IConfiguration config = new ConfigurationBuilder()
2: .AddJsonFile("KestrelServerOptions.json")
3: .Build();
4:
5: new WebHostBuilder()
6: .UseKestrel()
7: .ConfigureServices(services=>services.Configure<KestrelServerOptions>(config))
8: .Configure(app => app.Run(async context => await context.Response.WriteAsync("Hello World")))
9: .Build()
10: .Run();
We generally call the WebHostBuilder Extension Method UseKestrel to register KestrelServer. As shown in the following code snippet, The UseKestrel method has two reloads, one of which has a parameter of the same type as Action <KestrelServerOptions>. We can use this parameter to directly set KestrelServerOptions.
1: public static class WebHostBuilderKestrelExtensions
2: {
3: public static IWebHostBuilder UseKestrel(this IWebHostBuilder hostBuilder);
4: public static IWebHostBuilder UseKestrel(this IWebHostBuilder hostBuilder, Action<KestrelServerOptions> options);
5: }
Because the Server monitors, receives, and responds to requests, the Server is one of the factors that affect the overall Web application response capability and throughput. In order to use the Server more effectively, we often set specific network load conditions. For KestrelServer, The KestrelServerOptions object specified as the parameter in the constructor represents the settings for it. Our settings for KestrelServer are mainly reflected in the following five attributes of the KestrelServerOptions type.
1: public class KestrelServerOptions
2: {
3: // omit other members
4: public int MaxPooledHeaders { get; set; }
5: public int MaxPooledStreams { get; set; }
6: public bool NoDelay { get; set; }
7: public TimeSpan ShutdownTimeout { get; set; }
8: public int ThreadCount { get; set; }
9: }
Iii. ServerAddressesFeature
In the demo instance, we didn't actually specify a listening address for the registered KestrelServer. From the running effect, we can easily see that WebHost will specify "http: // localhost: 5000 "is the default listening address. The server listening address can be explicitly specified. Before introducing how to specify the listening address for the server by programming, let's first get to know a feature named ServerAddressesFeature.
We know that the server interface IServer defines a read-only attribute Features of the IFeatureCollection type, which is used to describe the feature set of the current server. ServerAddressesFeature is an important feature, it is included in this set. The ServerAddressesFeature object is a collectively referred to for all types that implement the IServerAddressesFeature interface and their corresponding objects. This interface has a unique read-only attribute to return to the server's listening address list. By default, the ServerAddressesFeature used by ASP. NET Core is of the same name type as defined below.
1: public interface IServerAddressesFeature
2: {
3: ICollection<string> Addresses { get; }
4: }
5:
6: public class ServerAddressesFeature : IServerAddressesFeature
7: {
8: public ICollection<string> Addresses { get; }
9: }
A ServerAddressesFeature object is contained by default in the feature set represented by its Features attribute on a server created by dependency injection. If no valid listening address is added to the address list of this ServerAddressesFeature object, WebHost will add explicitly specified addresses (one or more) to the list. The listener address we explicitly specify is actually saved on a Configuration object as the WebHost Configuration, and the Key of the Configuration item is "urls ", the static read-only attribute ServerUrlsKey of WebHostDefaults returns such a Key.
1: new WebHostBuilder()
2: .UseSetting(WebHostDefaults.ServerUrlsKey, "http://localhost:3721/")
3: .UseMyKestrel()
4: .UseStartup<Startup>()
5: .Build()
6: .Run();
The WebHost configuration was initially created by creating its WebHostBuilder. The latter provides a UseSettings method to set the value of a configuration item, so we can specify the listening address ("http: // localhost: 3721 /"). However, for explicit settings of the listener address, the most direct programming method is to call the extension method UseUrls of WebHostBuilder, as shown in the following code snippet. The implementation logic of this method is exactly the same as above.
1: public static class WebHostBuilderExtensions
2: {
3: public static IWebHostBuilder UseUrls(this IWebHostBuilder hostBuilder, params string[] urls)
4: =>hostBuilder.UseSetting(WebHostDefaults.ServerUrlsKey, string.Join(ServerUrlsSeparator, urls)) ;
5: }