泛型主机与生命周期
大约 10 分钟约 2908 字
泛型主机与生命周期
简介
ASP.NET Core 的泛型主机(Generic Host)是应用的运行时基础,管理依赖注入、配置、日志和托管服务。理解主机的构建过程、应用生命周期和 IHostedService,有助于正确管理应用启动和关闭流程。
特点
Host 构建过程
WebApplicationBuilder 内部
// ASP.NET Core 6+ 的最小化 API
var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
app.Run();
// 等价于(ASP.NET Core 5 风格):
// var host = Host.CreateDefaultBuilder(args)
// .ConfigureWebHostDefaults(webBuilder =>
// {
// webBuilder.UseStartup<Startup>();
// })
// .Build();
// host.Run();
// CreateBuilder 内部做了什么:
// 1. 创建 HostBuilderContext(配置和环境)
// 2. 加载配置(appsettings.json、环境变量、命令行)
// 3. 配置日志(Console、Debug、EventLog)
// 4. 设置默认 DI 容器
// 5. 注册 IHostedService(Kestrel 等)
// 6. 设置内容根目录
// 自定义 Host 构建
var builder = WebApplication.CreateBuilder(new WebApplicationOptions
{
Args = args,
ContentRootPath = Directory.GetCurrentDirectory(),
WebRootPath = "wwwroot",
EnvironmentName = Environments.Development
});
// 访问底层 Host
builder.Host.ConfigureServices(services =>
{
services.AddSingleton<IMyService, MyService>();
});
builder.Host.ConfigureAppConfiguration(config =>
{
config.AddJsonFile("custom.json", optional: true, reloadOnChange: true);
});应用生命周期
生命周期事件
// IHostApplicationLifetime — 应用生命周期事件
public class LifecycleLogger
{
private readonly ILogger<LifecycleLogger> _logger;
private readonly IHostApplicationLifetime _lifetime;
public LifecycleLogger(ILogger<LifecycleLogger> logger,
IHostApplicationLifetime lifetime)
{
_logger = logger;
_lifetime = lifetime;
// 应用启动完成
_lifetime.ApplicationStarted.Register(() =>
{
_logger.LogInformation("应用已启动,开始接收请求");
});
// 应用正在停止
_lifetime.ApplicationStopping.Register(() =>
{
_logger.LogInformation("应用正在停止,停止接收新请求");
});
// 应用已停止
_lifetime.ApplicationStopped.Register(() =>
{
_logger.LogInformation("应用已停止,所有请求处理完毕");
});
}
}
// 优雅关闭(Graceful Shutdown)
var app = builder.Build();
var lifetime = app.Services.GetRequiredService<IHostApplicationLifetime>();
lifetime.ApplicationStopping.Register(() =>
{
// 执行清理工作
CleanupResources();
});
// 配置关闭超时
builder.Host.ConfigureServices(services =>
{
services.Configure<HostOptions>(options =>
{
options.ShutdownTimeout = TimeSpan.FromSeconds(30); // 默认5秒
});
});启动顺序
// ASP.NET Core 启动顺序:
// 1. Build Host(构建主机)
// → 注册服务到 DI
// → 构建配置
// → 配置日志
// 2. Start Host(启动主机)
// → 启动所有 IHostedService
// → Kestrel 开始监听
// 3. ApplicationStarted 触发
// → 应用准备好接收请求
// 4. 请求处理(运行中)
// 5. 收到关闭信号(Ctrl+C / SIGTERM)
// → ApplicationStopping 触发
// → 停止接收新请求
// → 等待现有请求完成(超时后强制关闭)
// → 停止所有 IHostedService
// 6. ApplicationStopped 触发
// → 应用完全停止
// 自定义启动逻辑
app.MapGet("/ready", () => "Ready");
app.MapGet("/health", () => "Healthy");
// 启动时预热
app.Lifetime.ApplicationStarted.Register(() =>
{
// 预热缓存
var cache = app.Services.GetRequiredService<IMemoryCache>();
WarmUpCache(cache);
});IHostedService
后台任务服务
// IHostedService — 应用启动时开始运行的后台服务
public class PeriodicTaskService : IHostedService, IDisposable
{
private readonly ILogger<PeriodicTaskService> _logger;
private Timer? _timer;
public PeriodicTaskService(ILogger<PeriodicTaskService> logger)
{
_logger = logger;
}
public Task StartAsync(CancellationToken cancellationToken)
{
_logger.LogInformation("定时任务服务启动");
_timer = new Timer(DoWork, null, TimeSpan.Zero, TimeSpan.FromMinutes(5));
return Task.CompletedTask;
}
private void DoWork(object? state)
{
_logger.LogInformation("执行定时任务: {Time}", DateTime.UtcNow);
// 执行后台任务
}
public Task StopAsync(CancellationToken cancellationToken)
{
_logger.LogInformation("定时任务服务停止");
_timer?.Change(Timeout.Infinite, 0);
return Task.CompletedTask;
}
public void Dispose() => _timer?.Dispose();
}
// 注册
builder.Services.AddHostedService<PeriodicTaskService>();
// BackgroundService(推荐基类)
public class DataSyncService : BackgroundService
{
private readonly ILogger<DataSyncService> _logger;
private readonly IServiceProvider _sp;
public DataSyncService(ILogger<DataSyncService> logger, IServiceProvider sp)
{
_logger = logger;
_sp = sp;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
_logger.LogInformation("数据同步服务启动");
while (!stoppingToken.IsCancellationRequested)
{
try
{
// 创建 Scope 获取 Scoped 服务
using var scope = _sp.CreateScope();
var syncService = scope.ServiceProvider.GetRequiredService<ISyncService>();
await syncService.SyncAsync(stoppingToken);
}
catch (Exception ex)
{
_logger.LogError(ex, "数据同步失败");
}
await Task.Delay(TimeSpan.FromMinutes(10), stoppingToken);
}
_logger.LogInformation("数据同步服务停止");
}
}
// 使用队列的后台服务
public class EmailQueueService : BackgroundService
{
private readonly Channel<EmailMessage> _channel;
private readonly IServiceProvider _sp;
public EmailQueueService(IServiceProvider sp)
{
_sp = sp;
_channel = Channel.CreateBounded<EmailMessage>(1000);
}
public async Task EnqueueAsync(EmailMessage message)
=> await _channel.Writer.WriteAsync(message);
protected override async Task ExecuteAsync(CancellationToken ct)
{
await foreach (var message in _channel.Reader.ReadAllAsync(ct))
{
using var scope = _sp.CreateScope();
var emailService = scope.ServiceProvider.GetRequiredService<IEmailService>();
await emailService.SendAsync(message);
}
}
}
// 注册为 Singleton
builder.Services.AddSingleton<EmailQueueService>();
builder.Services.AddHostedService(sp => sp.GetRequiredService<EmailQueueService>());环境管理
多环境配置
Kestrel 服务器配置
性能调优
// Kestrel 是 ASP.NET Core 的默认 Web 服务器
// 生产环境需要根据硬件和负载进行调优
builder.WebHost.ConfigureKestrel(options =>
{
// 监听端口
options.ListenAnyIP(5000); // HTTP
options.ListenAnyIP(5001, listenOptions =>
{
listenOptions.UseHttps(); // HTTPS
});
// 最大请求体大小
options.Limits.MaxRequestBodySize = 50 * 1024 * 1024; // 50MB
// 请求头大小限制
options.Limits.MaxRequestHeadersTotalSize = 64 * 1024; // 64KB
options.Limits.MaxRequestHeaderCount = 100;
// 请求超时
options.Limits.RequestHeadersTimeout = TimeSpan.FromSeconds(30);
options.Limits.KeepAliveTimeout = TimeSpan.FromMinutes(2);
// 连接限制
options.Limits.MaxConcurrentConnections = 1000;
options.Limits.MaxConcurrentUpgradedConnections = 1000;
// HTTP/2 配置
options.Limits.Http2.MaxStreamsPerConnection = 100;
options.Limits.Http2.InitialConnectionWindowSize = 1024 * 1024; // 1MB
options.Limits.Http2.InitialStreamWindowSize = 1024 * 1024;
// IO 队列配置(影响线程调度)
options.IOQueueCount = Environment.ProcessorCount;
// 响应最小数据速率(防慢速攻击)
options.Limits.MinRequestBodyDataRate =
new MinDataRate(bytesPerSecond: 100, gracePeriod: TimeSpan.FromSeconds(10));
options.Limits.MinResponseDataRate =
new MinDataRate(bytesPerSecond: 100, gracePeriod: TimeSpan.FromSeconds(5));
});
// appsettings.json 配置 Kestrel
// {
// "Kestrel": {
// "Endpoints": {
// "Http": { "Url": "http://+:5000" },
// "Https": { "Url": "https://+:5001" }
// },
// "Limits": {
// "MaxConcurrentConnections": 1000,
// "MaxRequestBodySize": 52428800
// }
// }
// }HTTPS 与证书配置
// HTTPS 证书配置
builder.WebHost.ConfigureKestrel(options =>
{
options.ListenAnyIP(5001, listenOptions =>
{
listenOptions.UseHttps("certificate.pfx", "password");
});
// 或从证书存储加载
options.ListenAnyIP(5001, listenOptions =>
{
listenOptions.UseHttps(httpsOptions =>
{
httpsOptions.ServerCertificate = LoadCertificate();
httpsOptions.ClientCertificateMode = ClientCertificateMode.NoCertificate;
httpsOptions.CheckCertificateRevocation = true;
httpsOptions.SslProtocols = System.Security.Authentication.SslProtocols.Tls12 | SslProtocols.Tls13;
});
});
});
static X509Certificate2? LoadCertificate()
{
// 从证书存储加载
using var store = new X509Store(StoreName.My, StoreLocation.LocalMachine);
store.Open(OpenFlags.ReadOnly);
var certs = store.Certificates.Find(
X509FindType.FindByThumbprint,
"YOUR_CERT_THUMBPRINT", validOnly: true);
return certs.FirstOrDefault();
}Worker Service 模式
非HTTP应用托管
// Worker Service 模板 — 不需要 HTTP 服务器
// dotnet new worker -n MyWorker
// Program.cs
var builder = Host.CreateDefaultBuilder(args);
builder.ConfigureServices(services =>
{
services.AddHostedService<Worker>();
services.AddSingleton<IMyService, MyService>();
});
var host = builder.Build();
await host.RunAsync();
// Worker.cs
public class Worker : BackgroundService
{
private readonly ILogger<Worker> _logger;
private readonly IHostApplicationLifetime _lifetime;
public Worker(ILogger<Worker> logger, IHostApplicationLifetime lifetime)
{
_logger = logger;
_lifetime = lifetime;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
_logger.LogInformation("Worker 服务启动于 {Time}", DateTime.UtcNow);
while (!stoppingToken.IsCancellationRequested)
{
try
{
await DoWorkAsync(stoppingToken);
}
catch (Exception ex)
{
_logger.LogError(ex, "Worker 执行出错");
// 可选择停止应用
// _lifetime.StopApplication();
}
await Task.Delay(TimeSpan.FromSeconds(30), stoppingToken);
}
}
private async Task DoWorkAsync(CancellationToken ct)
{
// 具体业务逻辑
await Task.Delay(1000, ct);
}
}
// 适合场景:
// - 消息队列消费者
// - 定时任务调度器
// - 数据同步服务
// - 后台数据处理依赖注入生命周期
生命周期管理深入
// DI 生命周期与 HostedService 的交互
// ❌ 错误:在 Singleton 中注入 Scoped 服务
builder.Services.AddSingleton<BadService>(); // 如果构造函数需要 Scoped 服务会报错
// ✅ 正确:在 Singleton 中使用 IServiceProvider
builder.Services.AddSingleton<GoodService>(sp =>
{
// 不在构造函数中解析 Scoped 服务
return new GoodService(sp);
});
public class GoodService
{
private readonly IServiceProvider _sp;
public GoodService(IServiceProvider sp)
{
_sp = sp;
}
public void DoWork()
{
// 在方法中创建 Scope
using var scope = _sp.CreateScope();
var dbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
// 使用 dbContext...
}
}
// Scoped 服务的生命周期:每次 HTTP 请求一个实例
// 在 HostedService 中必须手动创建 Scope
public class ScopedBackgroundService : BackgroundService
{
private readonly IServiceProvider _sp;
public ScopedBackgroundService(IServiceProvider sp) => _sp = sp;
protected override async Task ExecuteAsync(CancellationToken ct)
{
while (!ct.IsCancellationRequested)
{
using var scope = _sp.CreateScope();
var service = scope.ServiceProvider.GetRequiredService<IScopedService>();
await service.ProcessAsync(ct);
await Task.Delay(TimeSpan.FromMinutes(5), ct);
}
}
}
// 验证 Scoped 服务未被错误注入
// 开发环境添加
builder.Host.UseDefaultServiceProvider(options =>
{
options.ValidateScopes = true; // 验证 Scope 正确性
options.ValidateOnBuild = true; // 构建时验证所有服务
});启动预热
应用就绪检查
// 启动预热:在 ApplicationStarted 之前完成关键初始化
public class StartupWarmupService : IHostedService
{
private readonly IServiceProvider _sp;
private readonly ILogger<StartupWarmupService> _logger;
public StartupWarmupService(IServiceProvider sp, ILogger<StartupWarmupService> logger)
{
_sp = sp;
_logger = logger;
}
public async Task StartAsync(CancellationToken ct)
{
_logger.LogInformation("开始启动预热...");
using var scope = _sp.CreateScope();
// 1. 验证数据库连接
var db = scope.ServiceProvider.GetRequiredService<AppDbContext>();
await db.Database.MigrateAsync(ct);
_logger.LogInformation("数据库连接验证完成");
// 2. 预热缓存
var cache = scope.ServiceProvider.GetRequiredService<IMemoryCache>();
var config = scope.ServiceProvider.GetRequiredService<IConfiguration>();
var hotKeys = config.GetSection("Warmup:CacheKeys").Get<List<string>>();
if (hotKeys != null)
{
foreach (var key in hotKeys)
{
cache.Set(key, await LoadCacheDataAsync(key, ct));
}
}
_logger.LogInformation("缓存预热完成");
// 3. 验证外部服务
var redis = scope.ServiceProvider.GetRequiredService<IConnectionMultiplexer>();
if (!redis.IsConnected)
{
throw new InvalidOperationException("Redis 连接失败");
}
_logger.LogInformation("外部服务验证完成");
_logger.LogInformation("启动预热完成");
}
public Task StopAsync(CancellationToken ct) => Task.CompletedTask;
private async Task<object> LoadCacheDataAsync(string key, CancellationToken ct)
{
// 加载缓存数据...
await Task.Delay(100, ct);
return new object();
}
}
// 注册 — 排在其他 HostedService 之前
builder.Services.AddHostedService<StartupWarmupService>();
builder.Services.AddHostedService<PeriodicTaskService>();// 环境名称
// Development — 开发
// Staging — 预发布
// Production — 生产
// 设置环境
// 方式 1:环境变量
// ASPNETCORE_ENVIRONMENT=Production
// 方式 2:命令行
// dotnet run --environment Production
// 方式 3:launchSettings.json
// 环境判断
var env = builder.Environment;
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseSwagger();
}
else
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
// 按环境加载配置
// appsettings.json — 基础配置
// appsettings.Development.json — 开发环境覆盖
// appsettings.Production.json — 生产环境覆盖
// 环境特定的启动逻辑
if (builder.Environment.IsDevelopment())
{
builder.Services.AddDatabaseDeveloperPageExceptionFilter();
}
// 自定义环境
// builder.Environment.EnvironmentName = "Staging";
// appsettings.Staging.json 自动加载优点
缺点
总结
WebApplicationBuilder 是 ASP.NET Core 6+ 的入口,内部加载配置、注册服务、配置日志。应用生命周期通过 IHostApplicationLifetime 管理:ApplicationStarted(启动完成)、ApplicationStopping(正在停止)、ApplicationStopped(已停止)。IHostedService 在主机启动时开始运行,BackgroundService 是推荐的基类。后台服务中访问 Scoped 服务需要 CreateScope()。优雅关闭通过 ShutdownTimeout(默认 5 秒)控制等待时间。环境通过 ASPNETCORE_ENVIRONMENT 变量设置,配置文件按环境自动加载覆盖。
关键知识点
- 先分清这个主题位于请求链路、后台任务链路还是基础设施链路。
- 服务端主题通常不只关心功能正确,还关心稳定性、性能和可观测性。
- 任何框架能力都要结合配置、生命周期、异常传播和外部依赖一起看。
项目落地视角
- 画清请求进入、业务执行、外部调用、日志记录和错误返回的完整路径。
- 为关键链路补齐超时、重试、熔断、追踪和结构化日志。
- 把配置与敏感信息分离,并明确不同环境的差异来源。
常见误区
- 只会堆中间件或组件,不知道它们在链路中的执行顺序。
- 忽略生命周期和线程池、连接池等运行时资源约束。
- 没有监控和测试就对性能或可靠性下结论。
进阶路线
- 继续向运行时行为、可观测性、发布治理和微服务协同深入。
- 把主题和数据库、缓存、消息队列、认证授权联动起来理解。
- 沉淀团队级模板,包括统一异常处理、配置约定和基础设施封装。
适用场景
- 当你准备把《泛型主机与生命周期》真正落到项目里时,最适合先在一个独立模块或最小样例里验证关键路径。
- 适合 API 服务、后台任务、实时通信、认证授权和微服务协作场景。
- 当需求开始涉及稳定性、性能、可观测性和发布流程时,这类主题会成为基础设施能力。
落地建议
- 先定义请求链路与失败路径,再决定中间件、过滤器、服务边界和依赖方式。
- 为关键链路补日志、指标、追踪、超时与重试策略。
- 环境配置与敏感信息分离,避免把生产参数写死在代码或镜像里。
排错清单
- 先确认问题发生在路由、模型绑定、中间件、业务层还是基础设施层。
- 检查 DI 生命周期、配置来源、序列化规则和认证上下文。
- 查看线程池、连接池、缓存命中率和外部依赖超时。
复盘问题
- 如果把《泛型主机与生命周期》放进你的当前项目,最先要验证的输入、输出和失败路径分别是什么?
- 《泛型主机与生命周期》最容易在什么规模、什么边界条件下暴露问题?你会用什么指标或日志去确认?
- 相比默认实现或替代方案,采用《泛型主机与生命周期》最大的收益和代价分别是什么?
