You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何针对特定连接字符串监控.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%时触发告警)。

注意事项

  1. 连接池的唯一标识是基于连接字符串的(.NET会忽略密码等敏感信息生成池键),所以确保目标数据库的连接字符串在项目里是统一的,避免生成多个不同的池。
  2. 在Azure Linux应用服务上,这些方案都能正常运行,不需要特殊配置,只要确保日志和指标能被Azure Monitor收集到(比如已经启用了Application Insights集成)。
  3. 尽量不要用反射去访问SqlConnectionPool的内部状态,虽然调试时能用,但生产环境可能因为SqlClient版本更新而失效。

备注:内容来源于stack exchange,提问作者mortenbock

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.04.15 15:09:38