.NET 8 OtlpExporter与Docker版Jaeger 1.58无法完成HTTP/2握手
.NET 8 ASP.NET Core 连接Jaeger OTLP gRPC 失败问题
环境配置
- Jaeger 1.58 通过Docker Desktop启动,命令:
docker run -d --name jaeger -e COLLECTOR_OTLP_ENABLED=true -p 16686:16686 -p 4317:4317 -p 4318:4318 jaegertracing/all-in-one
- Jaeger日志确认OTLP收集器启动:
2024-07-10 11:33:02 {"level":"info","ts":1720625582.0473268,"caller":"otlpreceiver@v0.104.0/otlp.go:102","msg":"Starting GRPC server","endpoint":"localhost:4317"} 2024-07-10 11:33:02 {"level":"info","ts":1720625582.0492482,"caller":"otlpreceiver@v0.104.0/otlp.go:152","msg":"Starting HTTP server","endpoint":"localhost:4318"}
- .NET 8 ASP.NET Core项目使用OtlpExporter,通过
OtlpExportProtocol.Grpc协议连接http://localhost:4317
错误详情
启用OTEL_DIAGNOSTICS.json日志后,收到如下gRPC连接错误:
2024-07-10T16:16:45.7292747Z:Exporter failed send data to collector to {0} endpoint. Data will not be sent. Exception: {1}{http://localhost:4317/}{Grpc.Core.RpcException: Status(StatusCode="Unavailable", Detail="Error starting gRPC call. HttpRequestException: An HTTP/2 connection could not be established because the server did not complete the HTTP/2 handshake. (InvalidResponse) HttpIOException: An HTTP/2 connection could not be established because the server did not complete the HTTP/2 handshake. (InvalidResponse) HttpIOException: The response ended prematurely while waiting for the next frame from the server. (ResponseEnded)", DebugException="System.Net.Http.HttpRequestException: An HTTP/2 connection could not be established because the server did not complete the HTTP/2 handshake. (InvalidResponse)") ---> System.Net.Http.HttpRequestException: An HTTP/2 connection could not be established because the server did not complete the HTTP/2 handshake. (InvalidResponse) ---> System.Net.Http.HttpIOException: An HTTP/2 connection could not be established because the server did not complete the HTTP/2 handshake. (InvalidResponse) ---> System.Net.Http.HttpIOException: The response ended prematurely while waiting for the next frame from the server. (ResponseEnded) at System.Net.Http.Http2Connection.<ReadFrameAsync>g__ThrowMissingFrame|61_1() at System.Net.Http.Http2Connection.ReadFrameAsync(Boolean initialFrame) at System.Net.Http.Http2Connection.ProcessIncomingFramesAsync() --- End of inner exception stack trace --- at System.Net.Http.Http2Connection.ThrowRequestAborted(Exception innerException) at System.Net.Http.Http2Connection.Http2Stream.SendDataAsync(ReadOnlyMemory`1 buffer, CancellationToken cancellationToken) at Grpc.Net.Client.Internal.StreamExtensions.WriteMessageAsync[TMessage](Stream stream, GrpcCall call, TMessage message, Action`2 serializer, CallOptions callOptions) at Grpc.Net.Client.Internal.PushUnaryContent`2.WriteMessageCore(Task writeMessageTask) at System.Net.Http.Http2Connection.Http2Stream.SendRequestBodyAsync(CancellationToken cancellationToken) at System.Net.Http.Http2Connection.SendAsync(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken) --- End of inner exception stack trace --- at System.Net.Http.Http2Connection.SendAsync(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken) at System.Net.Http.HttpConnectionPool.SendWithVersionDetectionAndRetryAsync(HttpRequestMessage request, Boolean async, Boolean doRequestAuth, CancellationToken cancellationToken) at System.Net.Http.RedirectHandler.SendAsync(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken) at Grpc.Net.Client.Balancer.Internal.BalancerHttpHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) at System.Net.Http.HttpMessageInvoker.<SendAsync>g__SendAsyncWithTelemetry|6_0(HttpMessageHandler handler, HttpRequestMessage request, CancellationToken cancellationToken) at Grpc.Net.Client.Internal.GrpcCall`2.RunCall(HttpRequestMessage request, Nullable`1 timeout) --- End of inner exception stack trace --- at Grpc.Net.Client.Internal.GrpcCall`2.GetResponseHeadersCoreAsync() at Grpc.Net.Client.Internal.HttpClientCallInvoker.BlockingUnaryCall[TRequest,TResponse](Method`2 method, String host, CallOptions options, TRequest request) at Grpc.Core.Interceptors.InterceptingCallInvoker.<BlockingUnaryCall>b__3_0[TRequest,TResponse](TRequest req, ClientInterceptorContext`2 ctx) at Grpc.Core.ClientBase.ClientBaseConfiguration.ClientBaseConfigurationInterceptor.BlockingUnaryCall[TRequest,TResponse](TRequest request, ClientInterceptorContext`2 context, BlockingUnaryCallContinuation`2 continuation) at Grpc.Core.Interceptors.InterceptingCallInvoker.BlockingUnaryCall[TRequest,TResponse](Method`2 method, String host, CallOptions options, TRequest request) at OpenTelemetry.Proto.Collector.Logs.V1.LogsService.LogsServiceClient.Export(ExportLogsServiceRequest request, CallOptions options) at OpenTelemetry.Proto.Collector.Logs.V1.LogsService.LogsServiceClient.Export(ExportLogsServiceRequest request, Metadata headers, Nullable`1 deadline, CancellationToken cancellationToken) at OpenTelemetry.Exporter.OpenTelemetryProtocol.Implementation.ExportClient.OtlpGrpcLogExportClient.SendExportRequest(ExportLogsServiceRequest request, DateTime deadlineUtc, CancellationToken cancellationToken)}
已尝试的无效操作
添加以下代码允许未加密HTTP/2连接,但问题依然存在:
AppContext.SetSwitch("System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport", true);
解决方案
1. 修改Jaeger监听地址
Jaeger日志显示gRPC服务监听localhost:4317,但容器内的localhost仅指向自身,导致宿主机请求无法被正确接收。修改启动命令,强制服务监听所有地址:
docker run -d --name jaeger \ -e COLLECTOR_OTLP_ENABLED=true \ -e OTEL_EXPORTER_OTLP_ENDPOINT=0.0.0.0:4317 \ -p 16686:16686 \ -p 4317:4317 \ -p 4318:4318 \ jaegertracing/all-in-one:1.58
2. 确保.NET配置顺序正确
Http2UnencryptedSupport开关必须在任何gRPC操作前设置,同时明确配置OtlpExporter参数:
// 必须放在应用启动最前端 AppContext.SetSwitch("System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport", true); var builder = WebApplication.CreateBuilder(args); builder.Services.AddOpenTelemetry() .WithTracing(tracing => tracing .AddAspNetCoreInstrumentation() .AddOtlpExporter(options => { options.Endpoint = new Uri("http://localhost:4317"); options.Protocol = OpenTelemetry.Exporter.OtlpExportProtocol.Grpc; })); var app = builder.Build(); // 中间件配置...
3. 临时切换HTTP协议测试
若gRPC问题仍未解决,可切换到HTTP协议验证数据通路:
.AddOtlpExporter(options => { options.Endpoint = new Uri("http://localhost:4318/v1/traces"); options.Protocol = OpenTelemetry.Exporter.OtlpExportProtocol.HttpProtobuf; })
4. 检查端口和防火墙
- 使用
netstat -ano | findstr :4317(Windows)或lsof -i :4317(Linux/macOS)确认端口未被占用 - 重启Docker Desktop修复端口映射异常
内容的提问来源于stack exchange,提问作者Jaron
相关产品推荐
相关产品推荐

