如何在Kestrel中配置回调检测客户端连接关闭(.NET6 gRPC场景)
Kestrel客户端连接关闭检测实现方案
.NET 6 下可以通过自定义Kestrel连接中间件的方式直接获取TCP连接的生命周期事件,无需依赖Kestrel内部未公开的API,具体实现如下:
步骤1:修改Kestrel配置,注入自定义连接中间件
直接在你现有的ConfigureEndpointDefaults配置块中添加连接中间件逻辑即可:
var hostBuilder = webHostBuilder.ConfigureKestrel(kestrelServerOptions => { kestrelServerOptions.ConfigureHttpsDefaults(httpsConnectionAdapterOptions => httpsConnectionAdapterOptions.SslProtocols = SslProtocols.Tls12); var certPath = Path.Combine(builder.Environment.ContentRootPath, "Certs", $"{environment}.pfx"); kestrelServerOptions.ConfigureEndpointDefaults(listenOptions => { listenOptions.UseHttps(certPath, password); logger.Debug($"Using {certPath} as the cert file."); logger.Debug("Configuring host to use HTTP/2 protocol."); listenOptions.Protocols = HttpProtocols.Http2; // 新增:注入自定义连接中间件,监听连接全生命周期 listenOptions.Use(async (connectionContext, next) => { // 连接建立时触发 logger.Debug($"客户端连接已建立,连接ID:{connectionContext.ConnectionId},远程地址:{connectionContext.RemoteEndPoint}"); try { // 执行后续中间件逻辑,等待连接处理完成 await next(); // 连接正常关闭时触发 logger.Debug($"客户端连接已正常关闭,连接ID:{connectionContext.ConnectionId}"); } catch (IOException ex) when (ex.InnerException is SocketException se && se.SocketErrorCode is SocketErrorCode.ConnectionReset or SocketErrorCode.ConnectionAborted) { // 客户端强制断开连接时触发 logger.Debug($"客户端连接已重置,连接ID:{connectionContext.ConnectionId},错误码:{se.SocketErrorCode}"); } catch (Exception ex) { logger.Error(ex, $"客户端连接处理异常,连接ID:{connectionContext.ConnectionId}"); throw; } finally { // 无论连接正常/异常关闭,最终都会触发此处逻辑 logger.Debug($"客户端连接资源已释放,连接ID:{connectionContext.ConnectionId}"); } }); }); logger.Debug("Reading config values for the server name and port."); var port = builder.Configuration.GetValue<int>("AppSettings:OperationsServerPort"); var address = IPAddress.Parse("0.0.0.0"); if (address != null) { logger.Debug($"Host will listen at https://{address}:{port}"); kestrelServerOptions.Listen(address, port); } else { logger.Error("DNS address for service host cannot be determined! Exiting..."); Environment.Exit(-1); } });
步骤2:gRPC服务请求级别的断开检测(可选,针对单个调用场景)
如果只需要在具体的gRPC服务方法中检测当前调用对应的客户端连接是否断开,可以直接监听HttpContext.RequestAborted取消令牌:
public override async Task<TestResponse> TestCall(TestRequest request, ServerCallContext context) { var httpContext = context.GetHttpContext(); // 注册取消回调,客户端断开时触发 using var cancelReg = httpContext.RequestAborted.Register(() => { logger.Debug($"gRPC调用 {context.Method} 已取消,客户端连接断开"); }); // 你的业务逻辑,也可以直接将令牌传入异步方法实现自动取消 await Task.Delay(10000, httpContext.RequestAborted); return new TestResponse(); }
注意事项
Microsoft.AspNetCore.Server.Kestrel.Connections、Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets下的类型多为内部API,无官方兼容性承诺,不建议在生产代码中直接引用- 自定义连接中间件拿到的是传输层TCP连接的生命周期事件,和上层协议无关,HTTP/2多路复用场景下,单个TCP连接会承载多个gRPC调用,TCP连接关闭时所有对应调用都会被取消
内容的提问来源于stack exchange,提问作者Steve Miller
相关产品推荐
相关产品推荐

