Förstå ASP.NET Core begäran och svar pipeline - Del 2: Server och Hosting Layer
Inledning
I del 1 utforskade vi den övergripande arkitekturen för ASP.NET Core begäran och svar pipeline. Nu kommer vi att dyka djupt in i grundskiktet: servern och hosting infrastruktur. Det är där allt börjar – där din ansökan kommer till liv och där råa nätverk förfrågningar omvandlas till den strukturerade HttpContext objekt som flyter genom din mellanutrustning pipeline.
Förstå detta lager är avgörande eftersom det styr hur din ansökan startar, hur den är konfigurerad, och hur den interagerar med den underliggande webbservern. Oavsett om du distribuerar till produktion, optimerar prestanda, eller konfigurera HTTPS, är värdskiktet där dessa problem behandlas.
OBS: Detta är en del av mina experiment med AI / ett sätt att spendera $ 1000 Calude Code Web krediter. Jag har matat detta en BUNCH av papper, min förståelse, frågor jag var tvungen att generera denna artikel. Det är roligt och fyller en lucka jag inte har sett fylld någon annanstans.
Arkitekturen Two-Layer: Värddator och server
ASP.NET Core separerar problemen med program hosting och webbservering i två olika lager:
- Värddatorn - Hanterar programlivslängd, konfiguration, beroendeinjektion och loggning
- Servern - Hanterar HTTP-kommunikation, lyssnar på förfrågningar och hanterar anslutningar
Denna separation ger flexibilitet: du kan byta servrar (Kestrel, HTTP.sys, IIS Integration) utan att ändra din programkod, eller köra din applikation i olika hosting-miljöer (konsol-app, Windows Service, systemd-demon) utan att ändra serverns konfiguration.
Värdskiktet
WebApplication och WebApplicationBuilder
I ASP.NET Core 6 och senare har värdmodellen förenklats med WebApplication och WebApplicationBuilder. Detta ersatte de äldre IHostBuilder och IWebHostBuilder mönster med ett mer strömlinjeformat API.
// Modern ASP.NET Core 8 application
var builder = WebApplication.CreateBuilder(args);
// Configure services during the build phase
builder.Services.AddControllers();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
// Build the application
var app = builder.Build();
// Configure middleware after building
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();
// Start the server and begin processing requests
app.Run();
Vad händer under WebApplication.CreateBuilder()?
När du ringer WebApplication.CreateBuilder(args), en betydande mängd initiering inträffar:
// Simplified view of what CreateBuilder does internally
public static WebApplicationBuilder CreateBuilder(string[] args)
{
var builder = new WebApplicationBuilder();
// 1. Configure the host defaults
// - Content root path (Directory.GetCurrentDirectory())
// - Load appsettings.json and appsettings.{Environment}.json
// - Load environment variables
// - Load command-line arguments
// - Setup default logging providers (Console, Debug, EventSource, EventLog on Windows)
// 2. Configure Kestrel as the default web server
builder.WebHost.UseKestrel();
// 3. Setup dependency injection container
// - Creates the IServiceCollection
// - Registers core services
// 4. Configure the environment
// - Sets ASPNETCORE_ENVIRONMENT (Development, Staging, Production)
// - Determines if running in development mode
// 5. Setup configuration system
// - Creates the IConfiguration hierarchy
// - Combines all configuration sources
return builder;
}
Anpassa värddatorn
Du har omfattande kontroll över hur värden är konfigurerad:
var builder = WebApplication.CreateBuilder(args);
// Configure Kestrel server options
builder.WebHost.ConfigureKestrel(serverOptions =>
{
serverOptions.Limits.MaxConcurrentConnections = 100;
serverOptions.Limits.MaxRequestBodySize = 10 * 1024 * 1024; // 10 MB
serverOptions.Limits.MinRequestBodyDataRate = new MinDataRate(
bytesPerSecond: 100,
gracePeriod: TimeSpan.FromSeconds(10)
);
});
// Add additional configuration sources
builder.Configuration.AddJsonFile("customsettings.json", optional: true);
builder.Configuration.AddEnvironmentVariables(prefix: "MYAPP_");
// Configure logging
builder.Logging.ClearProviders(); // Remove defaults
builder.Logging.AddConsole();
builder.Logging.AddDebug();
builder.Logging.SetMinimumLevel(LogLevel.Warning);
// Change the content root and web root
builder.Environment.ContentRootPath = "/custom/path";
builder.Environment.WebRootPath = "/custom/wwwroot";
var app = builder.Build();
Servicebehållaren
Beroendeinjektionsbehållaren är byggd under initieringen av värddatorn. Tjänster registrerade här finns tillgängliga under hela applikationen:
var builder = WebApplication.CreateBuilder(args);
// Singleton: One instance for the application lifetime
builder.Services.AddSingleton<IMyService, MyService>();
// Scoped: One instance per request
builder.Services.AddScoped<IRequestService, RequestService>();
// Transient: New instance every time it's requested
builder.Services.AddTransient<ITransientService, TransientService>();
// Configure options pattern
builder.Services.Configure<MyOptions>(
builder.Configuration.GetSection("MyOptions")
);
// Access configuration directly during service registration
var connectionString = builder.Configuration.GetConnectionString("DefaultConnection");
builder.Services.AddDbContext<MyDbContext>(options =>
options.UseSqlServer(connectionString)
);
var app = builder.Build();
Miljö och inställning
ASP.NET Core tillhandahåller ett sofistikerat konfigurationssystem som slår samman flera källor:
var builder = WebApplication.CreateBuilder(args);
// Configuration is loaded in this order (later sources override earlier):
// 1. appsettings.json
// 2. appsettings.{Environment}.json
// 3. User secrets (in Development environment only)
// 4. Environment variables
// 5. Command-line arguments
// Access configuration
var mySetting = builder.Configuration["MySection:MySetting"];
var myValue = builder.Configuration.GetValue<int>("MySection:MyValue");
// Check environment
if (builder.Environment.IsDevelopment())
{
// Development-specific configuration
builder.Services.AddDatabaseDeveloperPageExceptionFilter();
}
if (builder.Environment.IsProduction())
{
// Production-specific configuration
builder.Configuration.AddAzureKeyVault(/* ... */);
}
var app = builder.Build();
Serverskiktet: Kestrel
Kestrel är ASP.NET Core plattformsoberoende webbserver. Den är snabb, lätt och kapabel att hantera produktionsbelastningar. Att förstå Kestrels funktioner hjälper dig att optimera programmets prestanda och säkerhet.
Kestrels arkitektur
flowchart TD cc[Client Connection] ch["Connection Handler Layer<br/>• TLS/SSL Termination (if HTTPS)<br/>• Protocol Negotiation (HTTP/1.1, HTTP/2, HTTP/3)"] parser["HTTP Protocol Parser<br/>• Request Line Parsing (Method, Path, Protocol)<br/>• Header Parsing<br/>• Body Reading"] ctx["HttpContext Creation<br/>• Creates HttpContext object<br/>• Populates Request properties<br/>• Prepares Response object"] pipeline[Middleware Pipeline] cc --> ch --> parser --> ctx --> pipeline
Kestrels arkitektur - djupare dyk
För att förstå hur bytes blir en HttpContext som din middleware kan använda, det hjälper till att zooma in på Kestrel på internt flöde och ansvar.
flowchart LR
subgraph os[OS / Network Stack]
net[(TCP/UDP Sockets)]
end
subgraph kestrel[Kestrel Server]
accept["Connection Accept Loop<br/>(.NET Sockets)"]
subgraph connmw[Per-Connection Middleware]
tls["TLS Termination / ALPN<br/>(Selects HTTP/1.1 vs HTTP/2 vs HTTP/3)"]
limits["Connection & Request Limits<br/>(timeouts, sizes, rate limits)"]
logging[Connection Logging]
end
subgraph proto[Protocol Handlers]
h1["HTTP/1.1 Handler<br/>(keep-alive, chunked, pipelining)"]
h2["HTTP/2 Handler<br/>(multiplexing, HPACK)"]
h3["HTTP/3 Handler<br/>(QUIC, QPACK)"]
end
subgraph io[High-Perf IO]
pipes["System.IO.Pipelines<br/>(zero-copy buffers)"]
parser2[HTTP Parser]
end
features["Feature Mapping<br/>(IFeatureCollection)"]
ctxpool[HttpContext Pool]
appinvoke[IHttpApplication.ProcessRequestAsync]
end
app[Your Middleware Pipeline]
net --> accept --> connmw --> proto
proto --> io --> features --> ctxpool --> appinvoke --> app
sequenceDiagram
autonumber
participant C as Client
participant S as Socket
participant K as Kestrel
participant P as Protocol Handler
participant A as App (Middleware)
C->>S: Connect (TCP/QUIC)
S->>K: New connection accepted
K->>K: TLS handshake + ALPN
K->>P: Select protocol (HTTP/1.1, 2, or 3)
loop Read/Parse
P->>P: Read using System.IO.Pipelines
P->>P: Parse request line/headers/body
end
P->>K: Build features + rent HttpContext from pool
K->>A: ProcessRequestAsync(HttpContext)
A-->>K: Writes response via Pipes
K-->>C: Flush/Send response frames
Note over K,C: Backpressure applied when client is slow
Nyckel interner att veta:
- TLS- och ALPN-val avgör vilken protokollhanterare som körs för denna anslutning.
- System.IO.Pipelines stödjer all tolkning och skrivning för minimal tilldelning och hög genomströmning.
- Feature Kartläggning (IFeatureCollection) exponerar serverfunktioner på låg nivå för HttpContext utan att binda den till Kestrel- typer.
- HttpContext objekt poolas för att minska GC-trycket; de ska återställas och återanvändas per begäran.
- Mottryck appliceras genom Pipes när klienten inte kan läsa tillräckligt snabbt; server won-t over-buffer skriver.
- Tidsgränser och tidsgränser (hålla liv, headers, begära kroppsstorlek, HTTP/2 ström gränser) skyddar servern från slowloris och resursuttömmande.
- Gängning: de flesta arbeten körs på ThreadPool; protokollhanterare undviker per-request trådskapande och föredrar async fortsättningar.
Anpassa Kestrel- slutpunkter
Du kan ställa in vilka endpoints Kestrel lyssnar på och hur:
var builder = WebApplication.CreateBuilder(args);
builder.WebHost.ConfigureKestrel(options =>
{
// Listen on all network interfaces on port 5000 (HTTP)
options.Listen(IPAddress.Any, 5000);
// Listen on localhost port 5001 (HTTPS)
options.Listen(IPAddress.Loopback, 5001, listenOptions =>
{
listenOptions.UseHttps("certificate.pfx", "password");
});
// Listen on specific IP with HTTP/2
options.Listen(IPAddress.Parse("192.168.1.100"), 5002, listenOptions =>
{
listenOptions.Protocols = HttpProtocols.Http2;
});
// Unix domain socket (Linux/macOS)
options.ListenUnixSocket("/tmp/myapp.sock");
// Named pipe (Windows)
options.ListenNamedPipe("mypipename");
});
var app = builder.Build();
Alternativt kan du konfigurera endpoints via appsettings.json:
{
"Kestrel": {
"Endpoints": {
"Http": {
"Url": "http://localhost:5000"
},
"Https": {
"Url": "https://localhost:5001",
"Certificate": {
"Path": "certificate.pfx",
"Password": "your-password"
}
}
}
}
}
var builder = WebApplication.CreateBuilder(args);
// Endpoints are automatically configured from appsettings.json
// when you don't explicitly call ConfigureKestrel
var app = builder.Build();
Inställning av HTTPS
HTTPS är viktigt för produktionsprogram. Kestrel tillhandahåller flera sätt att konfigurera TLS:
var builder = WebApplication.CreateBuilder(args);
builder.WebHost.ConfigureKestrel(options =>
{
options.Listen(IPAddress.Any, 5001, listenOptions =>
{
// Option 1: Certificate from file
listenOptions.UseHttps("certificate.pfx", "password");
// Option 2: Certificate from store (Windows)
listenOptions.UseHttps(storeCert =>
{
storeCert.Subject = "localhost";
storeCert.Store = "My";
storeCert.Location = StoreLocation.CurrentUser;
storeCert.AllowInvalid = false; // Don't allow invalid certs
});
// Option 3: Development certificate
listenOptions.UseHttps(); // Uses development certificate in Development environment
// Option 4: Configure TLS details
listenOptions.UseHttps(httpsOptions =>
{
httpsOptions.ServerCertificate = LoadCertificate();
httpsOptions.ClientCertificateMode = ClientCertificateMode.RequireCertificate;
httpsOptions.CheckCertificateRevocation = true;
httpsOptions.SslProtocols = SslProtocols.Tls12 | SslProtocols.Tls13;
// Client certificate validation
httpsOptions.ClientCertificateValidation = (certificate, chain, errors) =>
{
// Custom validation logic
return errors == SslPolicyErrors.None;
};
});
});
});
var app = builder.Build();
Kestrel gränser och prestanda avstämning
Kestrel ger många alternativ för att styra resursanvändning och optimera prestanda:
var builder = WebApplication.CreateBuilder(args);
builder.WebHost.ConfigureKestrel(options =>
{
// Connection limits
options.Limits.MaxConcurrentConnections = 100;
options.Limits.MaxConcurrentUpgradedConnections = 100;
// Request limits
options.Limits.MaxRequestBodySize = 10 * 1024 * 1024; // 10 MB
options.Limits.MaxRequestHeaderCount = 100;
options.Limits.MaxRequestHeadersTotalSize = 32 * 1024; // 32 KB
options.Limits.MaxRequestLineSize = 8 * 1024; // 8 KB
// Keep-alive timeout
options.Limits.KeepAliveTimeout = TimeSpan.FromMinutes(2);
// Request header read timeout
options.Limits.RequestHeadersTimeout = TimeSpan.FromSeconds(30);
// Minimum data rate for request body
options.Limits.MinRequestBodyDataRate = new MinDataRate(
bytesPerSecond: 240,
gracePeriod: TimeSpan.FromSeconds(5)
);
// Minimum data rate for response body
options.Limits.MinResponseDataRate = new MinDataRate(
bytesPerSecond: 240,
gracePeriod: TimeSpan.FromSeconds(5)
);
// HTTP/2 specific limits
options.Limits.Http2.MaxStreamsPerConnection = 100;
options.Limits.Http2.HeaderTableSize = 4096;
options.Limits.Http2.MaxFrameSize = 16 * 1024; // 16 KB
options.Limits.Http2.MaxRequestHeaderFieldSize = 8 * 1024; // 8 KB
options.Limits.Http2.InitialConnectionWindowSize = 128 * 1024; // 128 KB
options.Limits.Http2.InitialStreamWindowSize = 96 * 1024; // 96 KB
});
var app = builder.Build();
Stöd för HTTP/2 och HTTP/3
Kestrel stöder moderna HTTP- protokoll:
var builder = WebApplication.CreateBuilder(args);
builder.WebHost.ConfigureKestrel(options =>
{
// HTTP/1.1 only
options.Listen(IPAddress.Any, 5000, listenOptions =>
{
listenOptions.Protocols = HttpProtocols.Http1;
});
// HTTP/1.1 and HTTP/2
options.Listen(IPAddress.Any, 5001, listenOptions =>
{
listenOptions.Protocols = HttpProtocols.Http1AndHttp2;
listenOptions.UseHttps();
});
// HTTP/2 only
options.Listen(IPAddress.Any, 5002, listenOptions =>
{
listenOptions.Protocols = HttpProtocols.Http2;
listenOptions.UseHttps();
});
// HTTP/3 (QUIC) - requires .NET 7+
options.Listen(IPAddress.Any, 5003, listenOptions =>
{
listenOptions.Protocols = HttpProtocols.Http1AndHttp2AndHttp3;
listenOptions.UseHttps();
});
});
var app = builder.Build();
Serverfunktioner
Kestrel exponerar serverfunktioner genom IFeatureCollection tillgängliga på HttpContext:
app.Use(async (context, next) =>
{
// Check if HTTP/2 is being used
var http2Feature = context.Features.Get<IHttpRequestFeature>();
if (http2Feature?.Protocol == "HTTP/2")
{
Console.WriteLine("Using HTTP/2");
}
// Access connection features
var connectionFeature = context.Features.Get<IHttpConnectionFeature>();
Console.WriteLine($"Remote IP: {connectionFeature?.RemoteIpAddress}");
Console.WriteLine($"Local IP: {connectionFeature?.LocalIpAddress}");
// TLS information
var tlsFeature = context.Features.Get<ITlsConnectionFeature>();
if (tlsFeature?.ClientCertificate != null)
{
Console.WriteLine($"Client cert: {tlsFeature.ClientCertificate.Subject}");
}
// Request body pipe for high-performance scenarios
var bodyPipeFeature = context.Features.Get<IRequestBodyPipeFeature>();
if (bodyPipeFeature != null)
{
var reader = bodyPipeFeature.Reader;
// Use System.IO.Pipelines for zero-copy reads
}
await next(context);
});
Tillämpning av händelser under livstid
Värden tillhandahåller krokar för applikationens livscykelhändelser:
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
// Get the application lifetime
var lifetime = app.Services.GetRequiredService<IHostApplicationLifetime>();
// Application started event
lifetime.ApplicationStarted.Register(() =>
{
Console.WriteLine("Application has started");
// Perform startup tasks (warm up caches, etc.)
});
// Application stopping event
lifetime.ApplicationStopping.Register(() =>
{
Console.WriteLine("Application is stopping");
// Begin graceful shutdown (stop accepting new requests)
});
// Application stopped event
lifetime.ApplicationStopped.Register(() =>
{
Console.WriteLine("Application has stopped");
// Cleanup resources
});
app.Run();
Du kan också implementera IHostedService För bakgrundsuppgifter:
public class MyBackgroundService : IHostedService, IDisposable
{
private Timer? _timer;
private readonly ILogger<MyBackgroundService> _logger;
public MyBackgroundService(ILogger<MyBackgroundService> logger)
{
_logger = logger;
}
public Task StartAsync(CancellationToken cancellationToken)
{
_logger.LogInformation("Background service is starting");
_timer = new Timer(DoWork, null, TimeSpan.Zero, TimeSpan.FromMinutes(5));
return Task.CompletedTask;
}
private void DoWork(object? state)
{
_logger.LogInformation("Background service is working");
// Perform periodic work
}
public Task StopAsync(CancellationToken cancellationToken)
{
_logger.LogInformation("Background service is stopping");
_timer?.Change(Timeout.Infinite, 0);
return Task.CompletedTask;
}
public void Dispose()
{
_timer?.Dispose();
}
}
// Register the service
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddHostedService<MyBackgroundService>();
var app = builder.Build();
Förtjusande avstängning
ASP.NET Core hanterar graciös avstängning automatiskt, men du kan anpassa beteendet:
var builder = WebApplication.CreateBuilder(args);
// Configure shutdown timeout
builder.WebHost.ConfigureKestrel(options =>
{
options.AddServerHeader = false; // Remove Server header for security
});
builder.Host.ConfigureHostOptions(options =>
{
// How long to wait for the application to shut down gracefully
options.ShutdownTimeout = TimeSpan.FromSeconds(30);
});
var app = builder.Build();
// During shutdown, Kestrel:
// 1. Stops accepting new connections
// 2. Waits for existing requests to complete (up to ShutdownTimeout)
// 3. Aborts remaining requests
// 4. Disposes services
// 5. Runs ApplicationStopped callbacks
app.Run();
Scenarier för produktionsvärdskap
Springa bakom en omvänd proxy
I produktionen ligger Kestrel vanligtvis bakom en omvänd proxy (nginx, Apache, IIS):
var builder = WebApplication.CreateBuilder(args);
// Configure forwarded headers for reverse proxy scenarios
builder.Services.Configure<ForwardedHeadersOptions>(options =>
{
options.ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto;
// If your proxy is on a known network
options.KnownNetworks.Add(new IPNetwork(IPAddress.Parse("10.0.0.0"), 8));
options.KnownProxies.Add(IPAddress.Parse("10.0.0.1"));
// Required when running in containers/Kubernetes
options.ForwardedHeaders = ForwardedHeaders.All;
options.KnownNetworks.Clear();
options.KnownProxies.Clear();
});
var app = builder.Build();
// Must be before other middleware
app.UseForwardedHeaders();
app.UseHttpsRedirection();
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();
app.Run();
Värddator för Windows- tjänst
var builder = WebApplication.CreateBuilder(args);
// Enable Windows Service lifetime
builder.Host.UseWindowsService();
// Configure content root for Windows Service
builder.Host.UseContentRoot(AppContext.BaseDirectory);
var app = builder.Build();
app.Run();
Linuxsystemd Service Hosting
var builder = WebApplication.CreateBuilder(args);
// Enable systemd lifetime
builder.Host.UseSystemd();
var app = builder.Build();
app.Run();
Avancerat: Anpassad server
Medan Kestrel är standardvalet, kan du implementera en anpassad server om det behövs:
public class CustomServer : IServer
{
private IFeatureCollection _features = new FeatureCollection();
public IFeatureCollection Features => _features;
public Task StartAsync<TContext>(IHttpApplication<TContext> application,
CancellationToken cancellationToken) where TContext : notnull
{
// Start listening for connections
// Create HttpContext for each request
// Invoke application.ProcessRequestAsync(httpContext)
return Task.CompletedTask;
}
public Task StopAsync(CancellationToken cancellationToken)
{
// Stop accepting new connections
// Wait for existing requests to complete
return Task.CompletedTask;
}
public void Dispose()
{
// Cleanup resources
}
}
// Use custom server
var builder = WebApplication.CreateBuilder(args);
builder.WebHost.UseServer(new CustomServer());
var app = builder.Build();
Nyckeluttagningar
- Värddatorn hanterar programlivslängd, konfiguration, DI och loggning
- Kestrel är en högpresterande, plattformsoberoende webbserver
- Du kan konfigurera endpoints, HTTPS, protokoll och prestandagränser
- Servern skapar
HttpContextobjekt som flyter genom mellanutrustning pipeline - Graceful avstängning säkerställer att begäran är fullständig innan ansökan avslutas
- Produktionsinstallationer använder normalt Kestrel bakom en omvänd proxy
- och
IFeatureCollectionger tillgång till serverfunktioner på låg nivå
Genom att förstå servern och värdlagret får du kontroll över hur din applikation startar, hur den hanterar anslutningar och hur den fungerar under belastning. Denna foundation stöder allt som händer i lagren ovan.