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

如何在两台Azure实例中实现一致的System.Runtime.Caching内存缓存(不使用Redis)

解决Azure多实例ASP.NET MVC应用内存缓存不一致问题(无需Redis)

针对Azure上多实例部署的ASP.NET MVC应用,使用System.Runtime.Caching内存缓存时出现的实例间缓存不一致问题,以下是几种无需Redis的可行解决方案:


1. 启用粘性会话(Affinity Cookie)

原理

让同一用户的所有请求始终路由到同一应用实例,确保用户只会访问该实例的本地缓存,避免跨实例的缓存不一致问题。

配置方式

在Azure App Service的配置>常规设置中,找到"粘性会话"选项并启用即可,无需修改代码。

优缺点

  • ✅ 配置简单,零代码改动
  • ❌ 实例故障时用户会切换到其他实例,导致缓存丢失;可能造成实例负载不均;无法解决跨用户的缓存一致性问题

2. 基于Azure存储的缓存同步(Blob/Table Storage)

思路

将缓存的源数据存储在Azure Blob或Table Storage中,每个实例的本地内存缓存从存储加载,并通过自定义变更监视器监听存储的变化,自动更新本地缓存。

实现要点

  • 封装统一的缓存操作类,读取缓存时优先查本地内存缓存,不存在则从Azure存储加载并写入本地
  • 自定义ChangeMonitor监听Azure存储的文件/数据变更,当源数据更新时,自动失效本地缓存

简化代码示例

// 自定义Blob变更监视器
public class BlobChangeMonitor : ChangeMonitor
{
    private readonly CloudBlob _targetBlob;
    private DateTimeOffset _lastModifiedTime;
    private Timer _monitorTimer;

    public BlobChangeMonitor(CloudBlob blob)
    {
        _targetBlob = blob;
        _lastModifiedTime = blob.Properties.LastModified.Value;
        StartMonitoring();
        InitializationComplete();
    }

    private void StartMonitoring()
    {
        // 每30秒检查一次Blob修改时间
        _monitorTimer = new Timer(async _ =>
        {
            await _targetBlob.FetchAttributesAsync();
            if (_targetBlob.Properties.LastModified.Value != _lastModifiedTime)
            {
                _lastModifiedTime = _targetBlob.Properties.LastModified.Value;
                OnChanged(null); // 触发缓存失效
            }
        }, null, TimeSpan.Zero, TimeSpan.FromSeconds(30));
    }

    protected override void Dispose(bool disposing)
    {
        _monitorTimer?.Dispose();
    }
}

// 使用缓存的示例方法
public object GetCachedData(string cacheKey)
{
    var memoryCache = MemoryCache.Default;
    var cachedItem = memoryCache.Get(cacheKey);

    if (cachedItem == null)
    {
        // 从Azure Blob加载源数据
        var blob = GetCloudBlobReference(cacheKey);
        cachedItem = DeserializeDataFromBlob(blob);
        
        // 设置缓存依赖,监听Blob变化
        var cachePolicy = new CacheItemPolicy
        {
            ChangeMonitors = { new BlobChangeMonitor(blob) }
        };
        memoryCache.Set(cacheKey, cachedItem, cachePolicy);
    }

    return cachedItem;
}

优缺点

  • ✅ 依赖Azure存储的高可用性,数据一致性较好
  • ❌ 缓存更新存在一定延迟;增加存储成本和网络开销

3. 使用SQL Server + SqlCacheDependency

思路

将缓存源数据存储在Azure SQL Server中,利用SqlCacheDependency监听数据库表的变更,当表数据更新时自动失效本地内存缓存,各实例重新加载最新数据。

配置步骤

  1. 启用SQL Server的Service Broker(需在数据库执行:ALTER DATABASE [YourDBName] SET ENABLE_BROKER;)
  2. 在ASP.NET项目中配置SqlCacheDependency,启用目标表的变更通知

简化代码示例

// 初始化SQL缓存依赖(可在Global.asax中执行)
string connectionString = ConfigurationManager.ConnectionStrings["CacheDB"].ConnectionString;
SqlCacheDependencyAdmin.EnableNotifications(connectionString);
SqlCacheDependencyAdmin.EnableTableForNotifications(connectionString, "CacheDataTable");

// 获取缓存数据
public object GetSqlBackedCachedData(string cacheKey)
{
    var memoryCache = MemoryCache.Default;
    var cachedItem = memoryCache.Get(cacheKey);

    if (cachedItem == null)
    {
        // 从SQL Server加载数据
        cachedItem = LoadDataFromSqlServer();
        
        // 设置SQL缓存依赖
        var cachePolicy = new CacheItemPolicy
        {
            ChangeMonitors = { new SqlChangeMonitor(new SqlCacheDependency("CacheDB", "CacheDataTable")) }
        };
        memoryCache.Set(cacheKey, cachedItem, cachePolicy);
    }

    return cachedItem;
}

优缺点

  • ✅ 数据一致性高,适合已有SQL Server的场景
  • ❌ 需要配置SQL Server Service Broker,可能增加数据库负载

4. 基于Azure Service Bus的缓存失效通知

思路

当任意实例更新缓存源数据时,通过Azure Service Bus发送缓存失效消息,其他实例订阅消息后立即清除对应本地缓存项,确保所有实例的缓存同步失效。

实现要点

  • 每个实例订阅Service Bus的主题/队列
  • 更新源数据后,发送包含缓存键的失效消息到Service Bus
  • 实例收到消息后,调用MemoryCache.Default.Remove()清除对应缓存

简化代码示例

// 初始化Service Bus订阅(实例启动时执行)
var subscriptionClient = new SubscriptionClient(ServiceBusConnectionString, "CacheInvalidationTopic", "InstanceSubscription");
subscriptionClient.RegisterMessageHandler(async (message, cancellationToken) =>
{
    var cacheKey = Encoding.UTF8.GetString(message.Body);
    MemoryCache.Default.Remove(cacheKey);
    await subscriptionClient.CompleteAsync(message.SystemProperties.LockToken);
}, new MessageHandlerOptions(ExceptionReceivedHandler) { MaxConcurrentCalls = 1 });

// 发送缓存失效消息(更新源数据时执行)
var topicClient = new TopicClient(ServiceBusConnectionString, "CacheInvalidationTopic");
var invalidationMessage = new Message(Encoding.UTF8.GetBytes("TargetCacheKey"));
await topicClient.SendAsync(invalidationMessage);

// 异常处理方法
private Task ExceptionReceivedHandler(ExceptionReceivedEventArgs args)
{
    // 处理消息接收异常
    return Task.CompletedTask;
}

优缺点

  • ✅ 缓存失效实时性好,能快速同步跨实例缓存状态
  • ❌ 需要额外配置Service Bus,增加成本和系统复杂度

方案选择建议

  • 若能接受用户固定实例的限制,优先选粘性会话,配置成本最低
  • 已有Azure SQL Server且要求高一致性,选SQL Server + SqlCacheDependency
  • 需要实时跨实例缓存同步,选Azure Service Bus失效通知
  • 以Azure存储作为数据存储载体,选Azure存储+自定义ChangeMonitor

内容的提问来源于stack exchange,提问作者Madhu Chepuroju

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.21 14:32:18