You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在不中断服务的情况下更新C#服务器应用程序?

无中断更新C#服务器应用的实践方案

核心思路:实现版本平滑过渡

本质是让新版本上线时,旧版本能继续处理现有客户端请求,直到所有连接自然结束或超时,同时新版本接管新请求,避免直接杀掉旧进程导致服务中断。

具体实现技巧

  • 反向代理流量切换:用Nginx、IIS ARR这类反向代理工具,更新时先启动新版本实例,通过健康检查确认服务可用后,逐步将流量切到新版本。旧版本保持运行,直到所有已连接客户端处理完请求再关停。C#应用要确保支持多实例运行,用户数据、会话不要存在进程内存里,改用分布式缓存或数据库共享。
  • 优雅关闭机制:在C#应用中监听进程关闭信号,停止接受新连接,等待现有请求处理完成再退出。比如ASP.NET Core可以用IHostApplicationLifetime实现:
var host = Host.CreateDefaultBuilder(args)
    .ConfigureServices(services => services.AddHostedService<GracefulShutdownService>())
    .Build();

host.Run();

public class GracefulShutdownService : IHostedService
{
    private readonly IHostApplicationLifetime _appLifetime;
    private readonly ILogger<GracefulShutdownService> _logger;

    public GracefulShutdownService(IHostApplicationLifetime appLifetime, ILogger<GracefulShutdownService> logger)
    {
        _appLifetime = appLifetime;
        _logger = logger;
    }

    public Task StartAsync(CancellationToken cancellationToken)
    {
        _appLifetime.ApplicationStopping.Register(() =>
        {
            _logger.LogInformation("触发优雅关闭,停止接受新连接");
            // 这里添加停止监听新连接的逻辑,比如关闭Socket或Kestrel的监听
            // 等待现有连接处理完成,设置合理超时(比如30秒)
            Task.Delay(TimeSpan.FromSeconds(30)).Wait();
            _logger.LogInformation("所有连接处理完毕,准备退出");
        });
        return Task.CompletedTask;
    }

    public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask;
}
  • 解耦进程内状态:如果应用把用户会话、临时数据存在内存,更新时会丢失状态。改用Redis、SQL Server等外部存储保存这些数据,让新旧实例都能访问,客户端切换到新版本后状态不会中断。
  • 滚动更新(集群场景):如果是多实例集群部署,每次只更新一个实例。等该实例启动并通过健康检查后,再更新下一个。这样始终有可用实例处理请求,完全避免服务中断。

关于UNIX Sockets传递更新数据的意见

UNIX Sockets是本地进程间通信的高效方案,适合在新旧进程间传递临时数据,但要注意以下几点:

  • 适用场景:单实例部署时,用来传递未处理完的请求上下文、临时状态,帮助新版本快速承接旧版本的工作。如果是多实例集群,更推荐用共享存储传递状态,比进程间通信更可靠。
  • 关键注意事项:
    • 新版本启动后要先监听UNIX Socket,再通知旧进程发送数据,避免旧进程发送时无接收方导致数据丢失。
    • 数据序列化用稳定格式,比如Protobuf、MessagePack,不要用JSON(性能差且易出现兼容性问题)。
    • 做降级处理:如果旧进程崩溃没发送数据,新版本要能从持久化存储恢复状态,不能完全依赖Socket传递的内容。
    • 跨平台兼容:Windows 10 1809+或Server 2019+才支持UNIX Sockets,若服务器可能跑Windows,建议用命名管道作为替代方案。
  • 简单实现示例:
// 旧进程发送数据
using var senderSocket = new Socket(AddressFamily.Unix, SocketType.Stream, ProtocolType.IP);
senderSocket.Connect(new UnixDomainSocketEndPoint("/tmp/app_update.sock"));
var payload = Encoding.UTF8.GetBytes("待传递的用户数据");
senderSocket.Send(payload);
senderSocket.Shutdown(SocketShutdown.Send);

// 新进程接收数据
using var listenerSocket = new Socket(AddressFamily.Unix, SocketType.Stream, ProtocolType.IP);
var endpoint = new UnixDomainSocketEndPoint("/tmp/app_update.sock");
listenerSocket.Bind(endpoint);
listenerSocket.Listen(1);
using var clientSocket = listenerSocket.Accept();
var buffer = new byte[1024];
var receivedBytes = clientSocket.Receive(buffer);
var receivedData = Encoding.UTF8.GetString(buffer, 0, receivedBytes);
// 处理接收的数据

内容的提问来源于stack exchange,提问作者Mohammed Sitni

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.11 05:10:29