在高并发服务运行过程中,直接终止进程会导致大量正在处理的请求被中断,数据库连接、缓存连接等资源没有正常释放,甚至会造成数据不一致的问题。c#实现优雅停机需要结合服务生命周期管理,在接收到停机信号后,先停止接收新的请求,等待现有请求处理完成,再释放相关资源后退出进程。

优雅停机核心流程
完整的优雅停机需要遵循以下四个步骤:
- 监听系统停机信号,比如Linux的SIGTERM、Windows的进程停止命令
- 停止接收新的外部请求,避免新请求进入处理队列
- 等待正在处理的请求全部完成,设置合理的等待超时时间
- 释放占用的资源,比如数据库连接、消息队列连接、文件句柄等,最后退出进程
ASP.NET Core中的优雅停机实现
ASP.NET Core内置了应用生命周期管理接口IHostApplicationLifetime,可以方便地监听应用启动和停止事件,结合请求处理控制实现优雅停机。
基础配置示例
首先在Program.cs中配置服务的停机相关参数,设置请求处理超时时间:
using Microsoft.AspNetCore.Server.Kestrel.Core;
var builder = WebApplication.CreateBuilder(args);
// 配置Kestrel服务器,设置停机时等待请求完成的超时时间
builder.WebHost.ConfigureKestrel(options =>
{
// 优雅停机等待时间,单位秒,这里设置为30秒
options.Limits.MaxRequestBodySize = 1024 * 1024 * 10;
});
// 注册应用生命周期事件
builder.Services.AddSingleton<IHostedService, GracefulShutdownService>();
var app = builder.Build();
// 模拟一个高并发处理的接口
app.MapGet("/process", async (HttpContext context) =>
{
// 模拟请求处理耗时,最长5秒
var random = new Random();
var delay = random.Next(1000, 5000);
await Task.Delay(delay);
return Results.Ok($"请求处理完成,耗时{delay}毫秒");
});
app.Run();
自定义优雅停机服务
通过实现IHostedService接口,自定义停机时的处理逻辑:
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
public class GracefulShutdownService : IHostedService
{
private readonly IHostApplicationLifetime _appLifetime;
private readonly ILogger<GracefulShutdownService> _logger;
// 用于标记是否已经开始停机流程
private bool _isShuttingDown = false;
// 正在处理的请求计数
private int _processingRequestCount = 0;
public GracefulShutdownService(IHostApplicationLifetime appLifetime, ILogger<GracefulShutdownService> logger)
{
_appLifetime = appLifetime;
_logger = logger;
}
public Task StartAsync(CancellationToken cancellationToken)
{
// 监听应用停止事件
_appLifetime.ApplicationStopping.Register(OnStopping);
_logger.LogInformation("优雅停机服务启动");
return Task.CompletedTask;
}
private void OnStopping()
{
if (_isShuttingDown)
{
return;
}
_isShuttingDown = true;
_logger.LogInformation("开始执行优雅停机流程,停止接收新请求");
// 等待正在处理的请求完成,最多等待30秒
var maxWaitTime = TimeSpan.FromSeconds(30);
var startTime = DateTime.Now;
while (_processingRequestCount > 0 && (DateTime.Now - startTime) < maxWaitTime)
{
Thread.Sleep(100);
}
if (_processingRequestCount > 0)
{
_logger.LogWarning($"优雅停机超时,仍有{_processingRequestCount}个请求未完成,强制退出");
}
else
{
_logger.LogInformation("所有请求处理完成,准备释放资源");
}
// 这里可以添加释放资源的逻辑,比如关闭数据库连接、断开消息队列连接等
ReleaseResources();
_logger.LogInformation("资源释放完成,应用即将退出");
}
private void ReleaseResources()
{
// 示例:释放数据库连接、缓存连接等资源
// DbContext.Dispose();
// RedisConnection.Close();
}
// 请求进入时增加计数,请求完成时减少计数,需要在中间件中调用
public void IncrementRequestCount()
{
if (!_isShuttingDown)
{
Interlocked.Increment(ref _processingRequestCount);
}
}
public void DecrementRequestCount()
{
Interlocked.Decrement(ref _processingRequestCount);
}
public bool IsShuttingDown => _isShuttingDown;
public Task StopAsync(CancellationToken cancellationToken)
{
_logger.LogInformation("优雅停机服务停止");
return Task.CompletedTask;
}
}
请求计数中间件
添加中间件来统计正在处理的请求数量,配合停机服务使用:
using Microsoft.AspNetCore.Http;
public class RequestCountMiddleware
{
private readonly RequestDelegate _next;
private readonly GracefulShutdownService _shutdownService;
public RequestCountMiddleware(RequestDelegate next, GracefulShutdownService shutdownService)
{
_next = next;
_shutdownService = shutdownService;
}
public async Task InvokeAsync(HttpContext context)
{
// 如果已经开始停机,直接返回服务不可用
if (_shutdownService.IsShuttingDown)
{
context.Response.StatusCode = 503;
await context.Response.WriteAsync("服务正在停机维护,请稍后重试");
return;
}
// 增加请求计数
_shutdownService.IncrementRequestCount();
try
{
await _next(context);
}
finally
{
// 请求处理完成后减少计数
_shutdownService.DecrementRequestCount();
}
}
}
// 中间件扩展方法
public static class RequestCountMiddlewareExtensions
{
public static IApplicationBuilder UseRequestCount(this IApplicationBuilder builder)
{
return builder.UseMiddleware<RequestCountMiddleware>();
}
}
在Program.cs中注册该中间件,注意要放在路由配置之前:
// 注册中间件
app.UseRequestCount();
// 之前的路由配置
app.MapGet("/process", async (HttpContext context) =>
{
var random = new Random();
var delay = random.Next(1000, 5000);
await Task.Delay(delay);
return Results.Ok($"请求处理完成,耗时{delay}毫秒");
});
通用控制台程序的优雅停机实现
如果是非ASP.NET Core的c#控制台高并发服务,可以通过监听系统信号实现优雅停机:
using System.Runtime.InteropServices;
class Program
{
private static bool _isShuttingDown = false;
private static int _processingTaskCount = 0;
private static readonly object _lockObj = new object();
static void Main(string[] args)
{
// 监听Linux SIGTERM信号
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
{
AppDomain.CurrentDomain.ProcessExit += (s, e) =>
{
StartGracefulShutdown();
};
}
// 监听Windows控制台关闭事件
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
SetConsoleCtrlHandler(ConsoleCtrlHandler, true);
}
// 启动工作线程模拟高并发任务处理
StartWorkerThreads();
// 等待停机信号
Console.WriteLine("服务启动完成,按Ctrl+C停止");
Console.ReadLine();
}
// Windows控制台事件处理
[DllImport("Kernel32")]
private static extern bool SetConsoleCtrlHandler(HandlerRoutine handler, bool add);
private delegate bool HandlerRoutine(int dwControlType);
private static bool ConsoleCtrlHandler(int dwControlType)
{
if (dwControlType == 2) // CTRL_CLOSE_EVENT
{
StartGracefulShutdown();
}
return false;
}
private static void StartGracefulShutdown()
{
if (_isShuttingDown)
{
return;
}
_isShuttingDown = true;
Console.WriteLine("开始执行优雅停机流程");
// 等待所有任务完成,最多等待30秒
var maxWaitTime = TimeSpan.FromSeconds(30);
var startTime = DateTime.Now;
while (_processingTaskCount > 0 && (DateTime.Now - startTime) < maxWaitTime)
{
Thread.Sleep(100);
}
if (_processingTaskCount > 0)
{
Console.WriteLine($"优雅停机超时,仍有{_processingTaskCount}个任务未完成");
}
else
{
Console.WriteLine("所有任务处理完成,准备退出");
}
// 释放资源
ReleaseResources();
Console.WriteLine("服务已停止");
Environment.Exit(0);
}
private static void ReleaseResources()
{
// 释放相关资源
}
private static void StartWorkerThreads()
{
for (int i = 0; i < 10; i++)
{
var threadId = i;
Task.Run(() =>
{
while (!_isShuttingDown)
{
// 模拟任务处理
Interlocked.Increment(ref _processingTaskCount);
try
{
var delay = new Random().Next(1000, 3000);
Thread.Sleep(delay);
Console.WriteLine($"线程{threadId}处理完成一个任务");
}
finally
{
Interlocked.Decrement(ref _processingTaskCount);
}
}
});
}
}
}
注意事项
- 停机等待时间需要结合业务实际设置,既不能太短导致请求未完成就被中断,也不能太长导致停机耗时过久
- 如果使用了负载均衡,在停机前可以先从负载均衡列表中摘除该节点,避免新请求继续转发过来
- 资源释放逻辑需要根据实际使用的组件调整,确保所有占用的资源都能被正确回收
- 测试时需要模拟高并发场景,验证停机过程中没有请求丢失,资源都能正常释放
C#Graceful_Shutdown高并发ASP.NET_Core修改时间:2026-07-11 02:54:15