.NET 7 Worker Service作为Windows服务运行异常求助
Windows服务模式下.NET 7 Worker Service代理功能失效问题排查与修复
我基于.NET 7.0开发了一个C# Worker Service程序,使用Titanium Web Proxy监听所有入站GET请求,并将请求的URI和URL保存到Log.txt文件中。程序直接运行时功能正常,但发布为Windows服务后,通过sc start命令启动服务,服务显示运行但实际功能完全不生效。
原始代码
Program.cs
using System; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using Serilog; using Serilog.Extensions.Hosting; using Serilog.Extensions.Logging; namespace WorkerService5 { public class Program { static async Task Main(string[] args) { IHost host = Host.CreateDefaultBuilder(args) .ConfigureServices(services => { services.AddHostedService<Worker>(); }) .UseWindowsService() .UseSerilog() .Build(); await host.RunAsync(); } } }
Worker.cs
using System.Net; using Titanium.Web.Proxy; using Titanium.Web.Proxy.EventArguments; using Titanium.Web.Proxy.Models; using Serilog; using Microsoft.Extensions.Hosting; namespace WorkerService5 { public class Worker : BackgroundService { private readonly ILogger<Worker> _logger; private readonly IHostApplicationLifetime _hostApplicationLifetime; public Worker(ILogger<Worker> logger, IHostApplicationLifetime hostApplicationLifetime) { _logger = logger; _hostApplicationLifetime = hostApplicationLifetime; } protected override async Task ExecuteAsync(CancellationToken stoppingToken) => Task.Run(async () => { try { var proxyServer = new ProxyServer(userTrustRootCertificate: false); proxyServer.BeforeRequest += OnRequest; var explicitEndPoint = new ExplicitProxyEndPoint(IPAddress.Any, 8000, true); proxyServer.AddEndPoint(explicitEndPoint); proxyServer.Start(); foreach (var endPoint in proxyServer.ProxyEndPoints) _logger.LogInformation("Listening on '{0}' endpoint at Ip {1} and port: {2} ", endPoint.GetType().Name, endPoint.IpAddress, endPoint.Port); proxyServer.SetAsSystemHttpProxy(explicitEndPoint); // Console.ReadLine(); stoppingToken.Register(() => { proxyServer.BeforeRequest -= OnRequest; proxyServer.Stop(); }); await Task.Delay(Timeout.Infinite, stoppingToken); } catch (Exception) { throw; } }); private async Task OnRequest(object sender, SessionEventArgs e) { var filePath = @"E:\LogiusService\Logs\log.txt"; Log.Logger = new LoggerConfiguration() .WriteTo.File(filePath, rollingInterval: RollingInterval.Day, shared: true) .CreateLogger(); string requestedUrl = e.HttpClient.Request.Host; Console.WriteLine("Requested URL: " + requestedUrl); Log.Information($"Requested URL: " + requestedUrl); string requestedUri = e.HttpClient.Request.RequestUri.AbsoluteUri; Console.WriteLine("Requested URI: " + requestedUri); Log.Information($"Requested URI: " + requestedUri); } } }
问题根源
- 权限不足:Windows服务默认以低权限账户运行,无修改系统代理、读写日志目录的权限
- Serilog重复初始化:每次请求时重新创建
Log.Logger,导致日志写入异常且效率低下 - 异步逻辑混乱:
ExecuteAsync嵌套Task.Run,破坏服务生命周期管理 - 系统代理设置失败:
SetAsSystemHttpProxy需要管理员权限,普通服务账户无法执行
修复方案
1. 修正Serilog全局配置(Program.cs)
将Serilog初始化提前绑定到Host,避免重复创建:
using System; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Serilog; namespace WorkerService5 { public class Program { static async Task Main(string[] args) { // 全局配置Serilog Log.Logger = new LoggerConfiguration() .WriteTo.File(@"E:\LogiusService\Logs\log.txt", rollingInterval: RollingInterval.Day, shared: true) .CreateLogger(); try { IHost host = Host.CreateDefaultBuilder(args) .ConfigureServices(services => { services.AddHostedService<Worker>(); }) .UseWindowsService() .UseSerilog() // 集成到Host日志系统 .Build(); await host.RunAsync(); } finally { Log.CloseAndFlush(); // 确保日志写入完成 } } } }
2. 重构Worker异步逻辑与权限处理(Worker.cs)
移除冗余嵌套、修复代理生命周期、添加错误日志:
using System.Net; using Titanium.Web.Proxy; using Titanium.Web.Proxy.EventArguments; using Titanium.Web.Proxy.Models; using Serilog; using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; namespace WorkerService5 { public class Worker : BackgroundService { private readonly ILogger<Worker> _logger; private ProxyServer? _proxyServer; public Worker(ILogger<Worker> logger) { _logger = logger; } protected override async Task ExecuteAsync(CancellationToken stoppingToken) { try { _proxyServer = new ProxyServer(userTrustRootCertificate: false); _proxyServer.BeforeRequest += OnRequest; var explicitEndPoint = new ExplicitProxyEndPoint(IPAddress.Any, 8000, true); _proxyServer.AddEndPoint(explicitEndPoint); _proxyServer.Start(); foreach (var endPoint in _proxyServer.ProxyEndPoints) _logger.LogInformation("Listening on '{0}' endpoint at Ip {1} and port: {2} ", endPoint.GetType().Name, endPoint.IpAddress, endPoint.Port); // 若不需要全局代理则注释此行,避免权限问题 // _proxyServer.SetAsSystemHttpProxy(explicitEndPoint); stoppingToken.Register(() => { if (_proxyServer != null) { _proxyServer.BeforeRequest -= OnRequest; _proxyServer.Stop(); } }); await Task.Delay(Timeout.Infinite, stoppingToken); } catch (Exception ex) { _logger.LogError(ex, "Proxy service encountered critical error"); throw; // 让服务管理器感知到错误 } } private async Task OnRequest(object sender, SessionEventArgs e) { if (e.HttpClient.Request.Method == "GET") { string requestedUrl = e.HttpClient.Request.Host; Log.Information($"Requested URL: {requestedUrl}"); string requestedUri = e.HttpClient.Request.RequestUri.AbsoluteUri; Log.Information($"Requested URI: {requestedUri}"); } await Task.CompletedTask; } } }
3. 服务部署权限配置
- 创建服务时指定管理员账户:
sc create ProxyWorkerService binPath= "C:\发布路径\WorkerService5.exe" obj= "DOMAIN\管理员账户" password= "账户密码" start= auto - 确保
E:\LogiusService\Logs目录存在,且服务账户拥有读写权限 - 若不需要全局代理,直接注释
SetAsSystemHttpProxy代码
验证步骤
- 重新发布程序
- 执行
sc start ProxyWorkerService启动服务 - 通过
http://localhost:8000发送GET请求,检查日志文件是否正常记录 - 查看Windows事件查看器的应用程序日志,排查潜在错误
内容的提问来源于stack exchange,提问作者Tomasz Florczyk
相关产品推荐
相关产品推荐

