我有一个服务,它在Linux下运行在SystemD下,但在Windows下的VS 22中编译和调试。该服务主要是一个MariaDB 10数据库的代理,该数据库被塑造为一个BackgroundWorker,通过SignalR服务于客户端。如果我在Windows上以释放模式运行它,逻辑线程的数量保持在一个合理的值(大约20-25)。请参见下面的图片。
在Linux下,几分钟后(我不能给你更多的洞察力不幸的是...我仍然必须找出什么可能会改变)线程的数量开始不断增加每一秒。
请看这里图片,已经到达100多个,而且还在继续计数:
读取current logical threads increasing / thread stack is leaking i得到了确认,如果其他线程没有完成,CLR允许新线程,但当前从Windows迁移到Linux时,代码没有变化。
这是调用SystemD的HostBuilder
public static IHostBuilder CreateWebHostBuilder(string[] args)
{
string curDir = MondayConfiguration.DefineCurrentDir();
IConfigurationRoot config = new ConfigurationBuilder()
// .SetBasePath(Directory.GetCurrentDirectory())
.SetBasePath(curDir)
.AddJsonFile("servicelocationoptions.json", optional: false, reloadOnChange: true)
#if DEBUG
.AddJsonFile("appSettings.Debug.json")
#else
.AddJsonFile("appSettings.json")
#endif
.Build();
return Host.CreateDefaultBuilder(args)
.UseContentRoot(curDir)
.ConfigureAppConfiguration((_, configuration) =>
{
configuration
.AddIniFile("appSettings.ini", optional: true, reloadOnChange: true)
#if DEBUG
.AddJsonFile("appSettings.Debug.json")
#else
.AddJsonFile("appSettings.json")
#endif
.AddJsonFile("servicelocationoptions.json", optional: false, reloadOnChange: true);
})
.UseSerilog((_, services, configuration) => configuration
.ReadFrom.Configuration(config, sectionName: "AppLog")// (context.Configuration)
.ReadFrom.Services(services)
.Enrich.FromLogContext()
.WriteTo.Console())
// .UseSerilog(MondayConfiguration.Logger)
.ConfigureServices((hostContext, services) =>
{
services
.Configure<ServiceLocationOptions>(hostContext.Configuration.GetSection(key: nameof(ServiceLocationOptions)))
.Configure<HostOptions>(opts => opts.ShutdownTimeout = TimeSpan.FromSeconds(30));
})
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
ServiceLocationOptions locationOptions = config.GetSection(nameof(ServiceLocationOptions)).Get<ServiceLocationOptions>();
string url = locationOptions.HttpBase + "*:" + locationOptions.Port;
webBuilder.UseUrls(url);
})
.UseSystemd();
}
与此同时,我试图跟踪所有的Monitor.Enter()
,我使用这些Monitor.Enter()
来呈现串行API端点,这些端点触及服务和内部结构的状态,但在Windows中似乎一切正常。
我开始怀疑调用SystemD的问题。我想知道调用UseSystemD()
真正涉及到什么,但周围没有这么多文档。我只是找到了Glenn Condron的[https://devblogs.microsoft.com/dotnet/net-core-and-systemd/](https://devblogs.microsoft.com/dotnet/net-core-and-systemd/)和MSDN上的一些快速说明。
编辑1:为了进一步调试,我创建了一个类来使用ClrMd扫描线程池。我的主服务有一个心跳(奇怪的是它被称为Ping),如下所示(不是添加到processTracker.Scan()):
private async Task Ping()
{
await _containerServer.SyslogQueue.Writer.WriteAsync((
LogLevel.Information,
$"Monday Service active at: {DateTime.UtcNow.ToLocalTime()}"));
string processMessage = ProcessTracker.Scan();
await _containerServer.SyslogQueue.Writer.WriteAsync((LogLevel.Information, processMessage));
_logger.DebugInfo()
.Information("Monday Service active at: {Now}", DateTime.UtcNow.ToLocalTime());
}
其中processTrackes id的结构如下:
public static class ProcessTracker
{
static ProcessTracker()
{
}
public static string Scan()
{
// see https://stackoverflow.com/questions/31633541/clrmd-throws-exception-when-creating-runtime/31745689#31745689
StringBuilder sb = new();
string answer = $"Active Threads{Environment.NewLine}";
// Create the data target. This tells us the versions of CLR loaded in the target process.
int countThread = 0;
var pid = Process.GetCurrentProcess().Id;
using (var dataTarget = DataTarget.AttachToProcess(pid, 5000, AttachFlag.Passive))
{
// Note I just take the first version of CLR in the process. You can loop over
// every loaded CLR to handle the SxS case where both desktop CLR and .Net Core
// are loaded in the process.
ClrInfo version = dataTarget.ClrVersions[0];
var runtime = version.CreateRuntime();
// Walk each thread in the process.
foreach (ClrThread thread in runtime.Threads)
{
try
{
sb = new();
// The ClrRuntime.Threads will also report threads which have recently
// died, but their underlying data structures have not yet been cleaned
// up. This can potentially be useful in debugging (!threads displays
// this information with XXX displayed for their OS thread id). You
// cannot walk the stack of these threads though, so we skip them here.
if (!thread.IsAlive)
continue;
sb.Append($"Thread {thread.OSThreadId:X}:");
countThread++;
// Each thread tracks a "last thrown exception". This is the exception
// object which !threads prints. If that exception object is present, we
// will display some basic exception data here. Note that you can get
// the stack trace of the exception with ClrHeapException.StackTrace (we
// don't do that here).
ClrException? currException = thread.CurrentException;
if (currException is ClrException ex)
sb.AppendLine($"Exception: {ex.Address:X} ({ex.Type.Name}), HRESULT={ex.HResult:X}");
// Walk the stack of the thread and print output similar to !ClrStack.
sb.AppendLine(" ------> Managed Call stack:");
var collection = thread.EnumerateStackTrace().ToList();
foreach (ClrStackFrame frame in collection)
{
// Note that CLRStackFrame currently only has three pieces of data:
// stack pointer, instruction pointer, and frame name (which comes
// from ToString). Future versions of this API will allow you to get
// the type/function/module of the method (instead of just the
// name). This is not yet implemented.
sb.AppendLine($" {frame}");
}
}
catch
{
//skip to the next
}
finally
{
answer += sb.ToString();
}
}
}
answer += $"{Environment.NewLine} Total thread listed: {countThread}";
return answer;
}
}
一切都很好,在Windows中,它在某种树文本视图中打印了很多不错的信息。
关键是它在某个地方需要Kernel32.dll,而在linux中是不可用的。有人能给点提示吗?服务是在没有.NET基础设施的情况下本地发布的,在发布模式下,arch linux 64,单个文件。
非常感谢Alex
1条答案
按热度按时间lstz6jyr1#
我发现了一种方法,可以跳过我需要从一个简单的调试会话的整个日志记录。我不知道我也可以远程连接到一个Systemd进程。只是按照https://learn.microsoft.com/en-us/visualstudio/debugger/remote-debugging-dotnet-core-linux-with-ssh?view=vs-2022的快速分步指南。唯一的先决条件是让服务处于调试模式,并在主机上安装NET运行时,但这真的是全部。抱歉,我以前不知道这一点。
亚历克斯