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

Azure函数应用使用托管身份实现SQL认证的性能问题及替代方案咨询

性能问题根因与优化方案

你当前代码的性能瓶颈非常明确:每次QuoteContext实例化时都会同步请求Azure AD访问令牌,单次令牌请求的网络开销就是你观察到的3秒额外耗时。同时你每次都新建DefaultAzureCredential实例,也浪费了类库本身内置的令牌缓存能力。

可行优化方案

方案1:官方推荐最简方案,用SQL驱动内置认证

只要你使用的Microsoft.Data.SqlClient版本≥2.1,直接修改连接字符串,添加Authentication=Active Directory Default参数即可,驱动会自动处理令牌的获取、缓存、刷新逻辑,完全不需要自己写代码处理AccessToken,性能最优。
示例连接字符串:

Server=tcp:<你的SQL服务器名>.database.windows.net,1433;Database=<你的数据库名>;Authentication=Active Directory Default;Connect Timeout=30;

修改后你原有的QuoteContext构造函数可以直接删除所有令牌相关逻辑,恢复为默认实现即可:

public QuoteContext(DbContextOptions options) : base(options)
{
    // 无需额外逻辑
}

方案2:自定义实现令牌缓存+连接拦截器

如果你因为版本限制不能用驱动内置认证,就用EF Core的连接拦截器+静态令牌缓存的方式实现,避免每次实例化DbContext都请求令牌:
首先实现自定义连接拦截器:

using Microsoft.EntityFrameworkCore.Diagnostics;
using Azure.Core;
using Azure.Identity;
using Microsoft.Data.SqlClient;

public class AzureSqlTokenInterceptor : DbConnectionInterceptor
{
    // 静态复用Credential,复用其内部缓存能力
    private static readonly DefaultAzureCredential _azureCredential = new DefaultAzureCredential();
    private static readonly TokenRequestContext _tokenRequestContext = new TokenRequestContext(
        new[] { "https://database.windows.net/.default" }
    );
    // 自定义缓存令牌,提前5分钟刷新避免过期
    private static AccessToken _cachedAccessToken;

    // 异步连接场景处理(绝大多数Web场景都是异步)
    public override async Task<InterceptionResult> ConnectionOpeningAsync(
        DbConnection connection,
        ConnectionEventData eventData,
        InterceptionResult result,
        CancellationToken cancellationToken = default)
    {
        if (connection is SqlConnection sqlConn && string.IsNullOrEmpty(sqlConn.AccessToken))
        {
            if (_cachedAccessToken.ExpiresOn <= DateTimeOffset.UtcNow.AddMinutes(5))
            {
                _cachedAccessToken = await _azureCredential.GetTokenAsync(_tokenRequestContext, cancellationToken);
            }
            sqlConn.AccessToken = _cachedAccessToken.Token;
        }
        return await base.ConnectionOpeningAsync(connection, eventData, result, cancellationToken);
    }

    // 同步连接场景处理,无同步需求可以省略
    public override InterceptionResult ConnectionOpening(
        DbConnection connection,
        ConnectionEventData eventData,
        InterceptionResult result)
    {
        if (connection is SqlConnection sqlConn && string.IsNullOrEmpty(sqlConn.AccessToken))
        {
            if (_cachedAccessToken.ExpiresOn <= DateTimeOffset.UtcNow.AddMinutes(5))
            {
                _cachedAccessToken = _azureCredential.GetToken(_tokenRequestContext, CancellationToken.None);
            }
            sqlConn.AccessToken = _cachedAccessToken.Token;
        }
        return base.ConnectionOpening(connection, eventData, result);
    }
}

然后在注册DbContext的时候注入拦截器即可:

services.AddDbContext<QuoteContext>(options =>
{
    options.UseSqlServer("你的连接字符串")
           .AddInterceptors(new AzureSqlTokenInterceptor());
});

额外注意:不要在DbContext的构造函数中执行任何IO操作,构造函数应当只处理简单的初始化逻辑,同步调用IO接口不仅会带来不必要的阻塞开销,高并发场景下还可能引发线程池耗尽问题。

内容的提问来源于stack exchange,提问作者Santosh Jindal

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.01 20:48:00