.NET 6环境下OpenTelemetry指标发送中断及gRPC通信异常问题排查求助
大家好,我在基于.NET 6开发的Web应用中使用OpenTelemetry做指标监控,最近遇到了几个棘手的问题,折腾了好一阵还是没彻底解决,想请教下社区的各位大佬有没有相关经验或者排查思路。
初始问题:指标突然停止发送,仅能通过重启应用池恢复
我的技术栈和配置如下:
- 框架:.NET 6.0
- 语言:C#
- OpenTelemetry版本:OpenTelemetry 1.6.0、OpenTelemetry.Exporter.OpenTelemetryProtocol 1.6.0
配置逻辑是在Program.cs中调用builder.Services.AddOpenTelemetry(builder.Configuration),并通过单独的MetricsConfigurator类做具体配置,核心代码片段如下:
private static readonly AsyncLocal<MetricStreamConfiguration> CurrentCallConfiguration = new(); public static MeterProvider Configure(IConfiguration configuration, Assembly mainAssembly) { var appName = mainAssembly.GetName(); var resource = ResourceBuilder.CreateDefault() .AddService(serviceName: appName.Name, serviceVersion: appName.Version!.ToString()) .AddAttributes(new KeyValuePair<string, object>[] { new("server.name", Environment.MachineName), new("process.id", Environment.ProcessId) }); var exportInterval = TimeSpan.FromMilliseconds(configuration.GetValue("OpenTelemetry:ExportIntervalMilliseconds", 60000)); return Sdk.CreateMeterProviderBuilder() .AddMeter("*") .SetResourceBuilder(resource) .AddView(instrument => { var config = CurrentCallConfiguration.Value; CurrentCallConfiguration.Value = null; return config; }) .AddOtlpExporter((eo, mo) => { eo.Endpoint = new Uri("http://localhost:5110"); eo.Protocol = OtlpExportProtocol.Grpc; mo.PeriodicExportingMetricReaderOptions = new PeriodicExportingMetricReaderOptions { ExportIntervalMilliseconds = (int)exportInterval.TotalMilliseconds }; mo.TemporalityPreference = MetricReaderTemporalityPreference.Delta; }).Build(); } public static void AddOpenTelemetry(this IServiceCollection services, IConfiguration configuration) { var meterProvider = Configure(configuration, Assembly.GetCallingAssembly()); services.AddSingleton(meterProvider); } public static Meter UsingConfiguration(this Meter meter, MetricStreamConfiguration configuration) { CurrentCallConfiguration.Value = configuration; return meter; }
已尝试的排查步骤
- 检查应用日志,无相关错误或警告
- 监控服务器资源使用,未发现异常
- 将所有OpenTelemetry相关包升级到最新版本
- 复查配置代码,未发现明显错误或冲突
- 指标停发时抓取内存dump,发现进程中没有OpenTelemetry实例;重启应用池后实例重新出现,指标恢复发送
尝试解决GC回收问题(更新10/04/2023)
我们推测是GC误将MeterProvider实例判定为无用对象提前回收,导致指标发送中断。于是做了如下调整:
新增静态字段保存MeterProvider实例,并使用GC.KeepAlive防止被回收,替换了之前的services.AddSingleton逻辑:
private static MeterProvider Provider; // 配置逻辑... Provider = Sdk.CreateMeterProviderBuilder() // 配置逻辑... .Build(); // 更多配置逻辑... GC.KeepAlive(Provider);
目前这个调整初步有效,但我不确定这是不是最优解——有没有更规范的方式来确保MeterProvider不被GC提前回收?另外也担心长期运行会不会导致内存膨胀?
新问题:gRPC通信异常(更新10/11/2023)
调整配置后部署到AWS测试环境,应用运行约3小时后出现新的错误:
2023-10-10T07:38:24.9429543Z: Exporter failed to send data to collector to {0} endpoint... Grpc.Core.RpcException: Status(StatusCode="Unavailable", Detail="Error starting gRPC call. HttpRequestException: An error occurred while sending the request. IOException: The request was aborted. IOException: The response ended prematurely while waiting for the next frame from the server.", DebugException="System.Net.Http.HttpRequestException: An error occurred while sending the request.")
应用和采集器部署在同一台机器上,网络问题的可能性很低。我们做了这些操作:
- 先后重启两台Web服务器的应用池,重启后指标发送恢复正常
- 检查采集器的活跃连接,重启前没有来自应用的进程,重启后出现
这个问题仅在**.NET 6 + IIS进程内托管 + AWS环境**下出现,指标本身采集正常,但和gRPC服务的通信会中断。目前我们在排查是不是采集器端的问题,以及聚焦于IOException中“响应提前结束等待服务器下一帧”的原因。
额外问题:日志写入时的TaskCancellationException
除此之外,还频繁遇到日志写入失败的问题,错误信息如下:
Failed to write logs to OpenTelemetry due to System.Threading.Tasks.TaskCanceledException: Reached deadline.
at Grpc.Core.Channel.d__16.MoveNext() in /path/obfuscated/Grpc.Core/Channel.cs:line 146
... [省略后续栈跟踪]
at Obfuscated.OpenTelemetryLogProvider.d__39.MoveNext() in /path/obfuscated/OpenTelemetryLogProvider.cs:line 250
这个错误出现频率不定,看起来和gRPC通道状态变更的机制有关,导致日志写入延迟或失败,对我们的运维监控影响很大。
求助方向
- 针对
MeterProvider被GC提前回收的问题,除了静态字段+GC.KeepAlive,有没有更符合.NET最佳实践的解决方案? - 对于同机器部署下出现的gRPC Unavailable错误和TaskCanceledException,有什么具体的排查思路或解决办法?
非常感谢大家的耐心和帮助,期待各位的宝贵建议!
备注:内容来源于stack exchange,提问作者Simone Spagna

