.Net Core 3.1 gRPC客户端使用未加密HTTP2连接报PROTOCOL_ERROR错误
问题根源
AspNet Core 3.1在未加密的HTTP端口上同时启用HTTP/1.1和HTTP/2协议时,默认依赖Upgrade请求头完成协议协商,而gRPC客户端默认会直接发送HTTP/2连接前导码(preface),不会主动发起协议升级请求,导致服务端无法识别请求格式,返回PROTOCOL_ERROR错误。
解决方案
方案1:调整服务端端口配置(推荐)
将REST API和gRPC API拆分到不同的明文端口监听,避免协议协商冲突:
- 给REST API分配单独端口,配置为仅支持
Http1协议 - 给gRPC API分配单独端口,配置为仅支持
Http2协议
服务端Kestrel配置示例:
public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) .ConfigureWebHostDefaults(webBuilder => { webBuilder.ConfigureKestrel(options => { // REST API端口,仅支持HTTP1 options.ListenAnyIP(16040, o => o.Protocols = HttpProtocols.Http1); // gRPC API端口,仅支持HTTP2 options.ListenAnyIP(16050, o => o.Protocols = HttpProtocols.Http2); // 原有HTTPS端口保留 options.ListenAnyIP(16150, o => o.UseHttps()); }); webBuilder.UseStartup<Startup>(); });
调整后客户端原有代码无需修改,直接访问16050端口即可正常调用gRPC服务。
方案2:保留单端口混布,调整客户端配置
如果必须在同一个明文端口同时提供HTTP1的REST服务和HTTP2的gRPC服务,可显式配置客户端发起协议升级请求:
static async Task Main(string[] args) { AppContext.SetSwitch("System.Net.Http.SocketsHttpHandler.Http2UnencryptedSupport", true); var channel = GrpcChannel.ForAddress("http://localhost:16050", new GrpcChannelOptions { HttpVersion = new Version(2, 0), // 允许客户端通过Upgrade机制协商升级到HTTP/2 HttpVersionPolicy = HttpVersionPolicy.RequestVersionOrLower }); var client = new EventService.EventServiceClient(channel); var reply1 = await client.GetTrackEventAsync(new GetTrackEventRequest { Name = "01Austra14" }); }
该方案会产生额外的协议协商网络开销,仅适合开发测试场景使用,不推荐生产环境部署。
内容的提问来源于stack exchange,提问作者Krusty
相关产品推荐
相关产品推荐

