Azure容器应用中通过Dapr调用.NET 8 gRPC服务失败
看起来你遇到的是Dapr与gRPC服务之间的协议不匹配问题,错误提示initial http2 frame from server is not a settings frame: *http2.GoAwayFrame通常意味着客户端(这里是Dapr或你的调用端)尝试建立HTTP/2连接,但服务器返回的响应不符合HTTP/2规范。我来帮你一步步排查和解决:
1. 修复调用端的Dapr端口混淆问题
你当前的代码里,获取了DAPR_GRPC_PORT并拼接成http://localhost:{daprPort},这里有个关键错误:DAPR_GRPC_PORT是Dapr用于接收gRPC调用的端口,直接用HTTP URL拼接会导致协议协商异常。另外要注意,通过Dapr调用gRPC服务时,必须显式配置GrpcChannel的HTTP/2支持。
修改后的调用代码:
var daprGrpcPort = Environment.GetEnvironmentVariable("DAPR_GRPC_PORT"); var url = string.IsNullOrEmpty(daprGrpcPort) ? this.myServiceUrl : $"http://localhost:{daprGrpcPort}"; // 显式配置GrpcChannel启用HTTP/2连接 var channel = GrpcChannel.ForAddress(url, new GrpcChannelOptions { HttpHandler = new SocketsHttpHandler { EnableMultipleHttp2Connections = true, }, }); var client = new MyService.MyServiceClient(channel); var myRequest = new MyRequest { DeviceId = request.DeviceId.ToString() }; // Dapr路由必须的元数据:指定目标服务的app-id var metadata = new Metadata { { "dapr-app-id", "myapp" } }; var response = await client.DoSomethingAsync(myRequest, metadata, cancellationToken: cancellationToken);
2. 强制后端gRPC服务使用HTTP/2
.NET 8的gRPC服务默认支持HTTP/2,但容器环境下Kestrel可能同时兼容HTTP/1.1,这会导致Dapr连接时出现协议协商问题。你需要显式配置Kestrel仅监听HTTP/2:
修改后端服务的启动代码:
var builder = WebApplication.CreateBuilder(args); // 配置Kestrel在8080端口强制使用HTTP/2 builder.WebHost.ConfigureKestrel(options => { options.ListenAnyIP(8080, o => o.Protocols = HttpProtocols.Http2); }); builder.Services.AddApplicationInsightsTelemetry(); builder.Services.AddGrpc(); var app = builder.Build(); app.MapGrpcService<MyService>(); app.MapGet("/", () => "Communication with gRPC endpoints must be made through a gRPC client."); app.Run();
3. 优化Dockerfile的容器配置
你的Dockerfile基础配置没问题,但可以添加环境变量明确指定Kestrel的监听端口,避免容器环境中的端口歧义:
在aspnet阶段添加环境变量:
FROM mcr.microsoft.com/dotnet/aspnet:8.0 EXPOSE 8080 # 明确指定Kestrel监听的端口 ENV ASPNETCORE_HTTP_PORTS=8080 WORKDIR /app COPY --from=build-env /app/out . ENTRYPOINT ["dotnet", "MyServiceApp.dll"]
4. 确认Dapr与Container Apps的配置匹配
你的Bicep配置已经做对了关键设置:
dapr.appProtocol: 'grpc':告诉Dapr用gRPC协议与后端服务通信ingress.transport: 'http2':确保内部 ingress 支持HTTP/2dapr.appPort: 8080:与后端服务的监听端口保持一致
这部分不需要修改,但可以利用你开启的enableApiLogging: true和logLevel: 'debug',查看Dapr sidecar的日志,确认它是否能成功连接到后端服务的8080端口,有没有连接超时或协议错误的细节。
最后验证步骤
- 重新构建后端服务的Docker镜像并部署到Azure Container Apps
- 部署调用端应用,确保Dapr sidecar正确注入
- 查看调用端和Dapr的日志,确认连接地址、协议配置和路由元数据都正确
这样应该就能解决HTTP/2协议不匹配的问题,让Dapr成功代理gRPC调用到你的后端服务。
备注:内容来源于stack exchange,提问作者fra

