如何针对特定连接字符串监控.NET 6 Azure Web应用的SQL连接池使用情况?
如何针对特定连接字符串监控.NET 6 Azure Web应用的SQL连接池使用情况?
碰到这种特定数据库的连接池超时问题确实闹心,默认的.NET事件计数器只会统计所有连接池的总和,根本没法定位到单个数据库的池状态。我给你几个实用的方案,都是在.NET 6和Azure环境下能直接落地的:
一、自己封装连接管理,手动统计特定池的连接数
最简单直接的方式是给特定数据库的连接做一层封装,在每次打开/关闭连接时维护计数器,同时记录关键状态。这样你能精准掌控这个数据库的连接池使用情况,还能轻松把数据打到日志或监控系统里。
比如写一个专门的连接工厂类:
public class SpecificDbConnectionFactory { private readonly string _connectionString; private readonly ILogger<SpecificDbConnectionFactory> _logger; private readonly SemaphoreSlim _semaphore = new SemaphoreSlim(1, 1); private int _currentInUseConnections; public SpecificDbConnectionFactory(string connectionString, ILogger<SpecificDbConnectionFactory> logger) { _connectionString = connectionString; _logger = logger; } public async Task<SqlConnection> GetOpenConnectionAsync(CancellationToken cancellationToken = default) { var connection = new SqlConnection(_connectionString); await connection.OpenAsync(cancellationToken); // 安全更新计数器 await _semaphore.WaitAsync(cancellationToken); _currentInUseConnections++; _semaphore.Release(); // 记录日志或发送自定义指标 _logger.LogInformation("特定数据库连接已打开,当前在用连接数:{Count}", _currentInUseConnections); // 如果用Application Insights,直接发送指标 // _telemetryClient.TrackMetric("SpecificDB_Connections_InUse", _currentInUseConnections); // 监听连接关闭事件,更新计数器 connection.StateChange += (sender, args) => { if (args.CurrentState is ConnectionState.Closed or ConnectionState.Broken) { _semaphore.Wait(); _currentInUseConnections--; _semaphore.Release(); _logger.LogInformation("特定数据库连接已关闭,当前在用连接数:{Count}", _currentInUseConnections); // _telemetryClient.TrackMetric("SpecificDB_Connections_InUse", _currentInUseConnections); } }; return connection; } }
之后项目里所有用到这个特定数据库的地方,都通过这个工厂类获取连接,就能实时监控它的连接使用趋势了。
二、监听SqlClient官方事件源,捕获特定池的操作
Microsoft.Data.SqlClient(.NET 6推荐使用这个库)自带了EventSource,会发出连接池相关的事件,比如连接从池取出、放回的操作。你可以写一个EventListener来监听这些事件,然后过滤出目标数据库的池数据。
示例代码如下:
public class SqlPoolEventListener : EventListener { private readonly string _targetDbName; private readonly ILogger<SqlPoolEventListener> _logger; private readonly Dictionary<string, int> _poolConnectionCounts = new(); public SqlPoolEventListener(string targetDbName, ILogger<SqlPoolEventListener> logger) { _targetDbName = targetDbName; _logger = logger; } protected override void OnEventSourceCreated(EventSource eventSource) { // 只监听SqlClient的事件源 if (eventSource.Name == "Microsoft.Data.SqlClient.EventSource") { EnableEvents(eventSource, EventLevel.Informational, EventKeywords.All); } } protected override void OnEventWritten(EventWrittenEventArgs eventArgs) { // 处理连接池打开和关闭事件 switch (eventArgs.EventName) { case "ConnectionPoolOpen": ProcessPoolOpen(eventArgs); break; case "ConnectionPoolClose": ProcessPoolClose(eventArgs); break; } } private void ProcessPoolOpen(EventWrittenEventArgs args) { var connStr = args.Payload?[0] as string; if (string.IsNullOrEmpty(connStr)) return; var builder = new SqlConnectionStringBuilder(connStr); if (builder.InitialCatalog != _targetDbName) return; // 生成连接池的唯一标识(去掉敏感信息) var poolKey = $"{builder.DataSource}_{builder.InitialCatalog}"; lock (_poolConnectionCounts) { _poolConnectionCounts.TryGetValue(poolKey, out var count); _poolConnectionCounts[poolKey] = count + 1; } _logger.LogInformation("目标数据库池{PoolKey}新增连接,当前连接数:{Count}", poolKey, _poolConnectionCounts[poolKey]); } private void ProcessPoolClose(EventWrittenEventArgs args) { var connStr = args.Payload?[0] as string; if (string.IsNullOrEmpty(connStr)) return; var builder = new SqlConnectionStringBuilder(connStr); if (builder.InitialCatalog != _targetDbName) return; var poolKey = $"{builder.DataSource}_{builder.InitialCatalog}"; lock (_poolConnectionCounts) { if (_poolConnectionCounts.TryGetValue(poolKey, out var count) && count > 0) { _poolConnectionCounts[poolKey] = count - 1; } } _logger.LogInformation("目标数据库池{PoolKey}释放连接,当前连接数:{Count}", poolKey, _poolConnectionCounts[poolKey]); } }
然后在Program.cs里注册这个监听器:
builder.Services.AddSingleton<SqlPoolEventListener>(sp => new SqlPoolEventListener("你的目标数据库名", sp.GetRequiredService<ILogger<SqlPoolEventListener>>()));
这样就能精准捕获目标数据库连接池的每一次操作,不会和其他数据库的池数据混在一起。
三、用Azure Application Insights做长期趋势监控
如果想做长期的趋势分析和告警,直接把自定义指标发送到Azure Application Insights是最方便的。结合上面的连接工厂或者事件监听,每次连接状态变化时调用TelemetryClient.TrackMetric,就能在Azure Portal里看到这个特定数据库的连接池使用曲线,还能设置阈值告警(比如连接数超过最大池大小的80%时触发告警)。
注意事项
- 连接池的唯一标识是基于连接字符串的(.NET会忽略密码等敏感信息生成池键),所以确保目标数据库的连接字符串在项目里是统一的,避免生成多个不同的池。
- 在Azure Linux应用服务上,这些方案都能正常运行,不需要特殊配置,只要确保日志和指标能被Azure Monitor收集到(比如已经启用了Application Insights集成)。
- 尽量不要用反射去访问SqlConnectionPool的内部状态,虽然调试时能用,但生产环境可能因为SqlClient版本更新而失效。
备注:内容来源于stack exchange,提问作者mortenbock
相关产品推荐
相关产品推荐

