.NET无加密gRPC跨机器HTTP/2连接失败求助
问题概述
在.NET 7环境中搭建无加密gRPC服务,本地客户端连接localhost或本地网卡的服务端可正常通信,但跨机器连接时抛出异常:
Error starting gRPC Call. HttpRequestException: Requesting HTTP version 2.0 with version policy RequestVersionOrHigher while unable to establish HTTP/2 connection.
服务端已配置Kestrel监听任意IP的HTTP/2端口,客户端已开启System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport开关并使用ChannelCredentials.Insecure,但日志显示检测到代理配置,推测是代理影响了跨机器连接,需求是让gRPC忽略代理直接建立连接。
相关代码与日志
服务端代码
var builder = WebApplication.CreateBuilder(args); builder.Logging.ClearProviders(); builder.Host.UseNLog(); builder.Services.AddGrpc(); builder.Services.AddScoped<ICredentialManager, CredentialManager>(); builder.WebHost.ConfigureKestrel(options => { // Setup a HTTP/2 endpoint without TLS. options.Listen(System.Net.IPAddress.Any, ServerPort, listenOptions => listenOptions.Protocols = HttpProtocols.Http2); }); var app = builder.Build(); // Configure the HTTP request pipeline. app.MapGrpcService<LoginService>(); ... other services ... app.MapGrpcService<TestingService>(); app.MapGet("/", () => "Communication with gRPC endpoints must be made through a gRPC client."); app.Run();
客户端代码
options = new GrpcChannelOptions(); options.LoggerFactory = new NLog.Extensions.Logging.NLogLoggerFactory(); options.Credentials = ChannelCredentials.Insecure; options.HttpHandler = new SocketsHttpHandler() { // Potentially set options here. No option for http versions available, though. }; AppContext.SetSwitch("System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport", true); // Only setting HttpHandler is supported by gRPC - setting default HttpClient causes exception; setting both, too. // options.HttpClient = new HttpClient() { BaseAddress = new Uri(address), DefaultRequestVersion = new Version(2, 0) }; Channel = GrpcChannel.ForAddress(address, options); var client = new Protos.Login.LoginClient(Channel);
客户端日志片段
2023-12-05 13:32:13.1584|INFO|Grpc.Net.Client.GrpcChannel|Proxy configuration is detected. How the gRPC client creates connections can cause unexpected behavior when a proxy is configured. To ensure the client correctly uses a proxy, configure GrpcChannelOptions.HttpHandler to use HttpClientHandler. Note that HttpClientHandler isn't compatible with load balancing. 2023-12-05 13:32:13.2631|WARN|OptionHunter.Program|first-chance thread exception (Requesting HTTP version 2.0 with version policy RequestVersionOrHigher while unable to establish HTTP/2 connection.)
解决方案
1. 配置SocketsHttpHandler直接禁用代理
修改客户端的SocketsHttpHandler配置,明确禁用代理并指定HTTP/2版本策略,确保客户端直接与服务端建立连接:
options = new GrpcChannelOptions(); options.LoggerFactory = new NLog.Extensions.Logging.NLogLoggerFactory(); options.Credentials = ChannelCredentials.Insecure; options.HttpHandler = new SocketsHttpHandler() { UseProxy = false, // 禁用代理,直接连接服务端 DefaultRequestVersion = HttpVersion.Version20, DefaultVersionPolicy = HttpVersionPolicy.RequestVersionOrHigher }; AppContext.SetSwitch("System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport", true); Channel = GrpcChannel.ForAddress(address, options); var client = new Protos.Login.LoginClient(Channel);
2. 清除系统代理环境变量(可选)
如果客户端机器存在全局代理环境变量(如HTTP_PROXY、HTTPS_PROXY),可以在创建gRPC通道前临时清除这些变量,避免被自动读取:
// 在初始化GrpcChannel前执行 Environment.SetEnvironmentVariable("HTTP_PROXY", null); Environment.SetEnvironmentVariable("HTTPS_PROXY", null);
3. 验证服务端网络连通性
确认服务端的指定端口未被防火墙、安全组拦截,可通过telnet 服务端IP 端口或nc -zv 服务端IP 端口命令测试跨机器端口是否可达。
原因说明
代理服务器通常默认不支持无加密的HTTP/2连接,客户端尝试通过代理建立HTTP/2连接时会失败,进而触发版本不匹配的异常。直接禁用代理后,客户端会直接与服务端建立无加密的HTTP/2连接,解决跨机器通信问题。
内容的提问来源于stack exchange,提问作者jowo

