1

我们一直在将我们的服务和 MVC4 网站转移到云端,总的来说这个过程很顺利。除了缓存之外,由于我们已经迁移到 Azure,因此使用 azure 提供的某种缓存也是明智之举。我们选择共同定位/专用缓存角色,其优点是缓存可用于所有实例。

设置缓存工作正常,我有一个命名缓存客户端,我只在需要时对其进行初始化。它设置在控制器的继承层中。一旦调用其中一个函数,它就会检查与数据缓存的连接是否仍然存在或已创建。这一切似乎都很好,但我正在构建一个模块来检索价格。并且多个 ajax 插入(使用 javascript 插入页面的视图)使用这些函数,其中一些被多个 ajax 视图同时调用。其中一些视图然后返回 404 或 500 错误,除了非工作缓存或类似情况外,我无法解释这些视图的来源。

有人可以帮助我很好地实现命名缓存(共同定位或专用),因为我所能找到的只是说明 DataCacheFactory 初始化的许多示例,而不是数据插入和检索的示例。

下面是我现在拥有的代码,我尝试了更多使用锁定等的方法,但到目前为止这个方法效果最好。

private static object magicStick = new object();

    private static DataCacheFactory dcf = null;
    private static DataCache priceCache = null;

    protected void CreateCacheFactory()
    {
        dcf = new DataCacheFactory();

    }
    protected void CreatePricesCache()
    {
        if (dcf == null)
        {
            CreateCacheFactory();
        }
        priceCache = dcf.GetCache("Prices");

    }
protected PriceData GetPrices(int productID)
{
    if (priceCache == null)
    {
        CreatePricesCache();
    }
    string cacheKey = "something";
    lock (magicStick)
    {
        PriceData datas = priceCache.Get(cacheKey) as PriceData;
        if (datas == null)
        {
            lock (magicStick)
            {
                Services svc = new Services();
                PriceData pData = svc.PriceService.GetPrices(productID);
                if (pData != null && pData.Offers != null && pData.Offers.Count() > 0)
                {
                    datas = pData;
                    datas.Offers = datas.Offers.OrderBy(pr => (pr.BasePrice + pr.ShippingCosts)).ToArray();
                    priceCache.Add(cacheKey, datas, new TimeSpan(0, cachingTimePricesKK, 0));
                }
            }
        } 
        return datas;
    }
}

一旦我进入一个有价目表的页面,并且使用相同的参数多次调用上述函数,它就有 5-10% 的机会返回错误而不是返回结果。任何人都可以帮助我,我现在完全坚持了一个星期,它在里面把我吃掉了。

4

1 回答 1

1

首先,我会将您的缓存和 cacheFactory 实例化移出您的 getPrices 方法。此外,评估您对锁的需求 - 这可能会导致超时。另一个非常重要的观察 - 您正在使用常量缓存键并为具有相同缓存键的每个 productId 保存/检索数据。您应该使用像这样的缓存键:var cacheKey = string.format("priceDatabyProductId-{0}", productId);. 您需要设置一些断点并准确检查您正在缓存和从缓存中检索的内容。编写的代码会将第一个 productId 保存到缓存中,然后不管 productId 继续返回该数据。

这是一个完整的工作示例,我们在生产中使用专用缓存角色中的“默认”命名缓存:

public static class MyCache
{
    private static DataCacheFactory _cacheFactory = null;
    private static DataCache ACache
    {
        get
        {
            if (_cacheFactory == null)
            {
                try
                {
                    _retryPolicy.ExecuteAction(() => { _cacheFactory = new DataCacheFactory(); });
                    return _cacheFactory == null ? null : _cacheFactory.GetDefaultCache();
                }
                catch (Exception ex)
                {
                    ErrorSignal.FromCurrentContext().Raise(ex);
                    return null;
                }
            }

            return _cacheFactory.GetDefaultCache();
        }
    }

    public static void FlushCache()
    {
        ACache.Clear();
    }

    // Define your retry strategy: retry 3 times, 1 second apart.
    private static readonly FixedInterval  _retryStrategy = new FixedInterval(3, TimeSpan.FromSeconds(1));

    // Define your retry policy using the retry strategy and the Windows Azure storage
    // transient fault detection strategy.
    private static RetryPolicy _retryPolicy = new RetryPolicy<StorageTransientErrorDetectionStrategy>(_retryStrategy);

    // Private constructor to prevent instantiation
    // and force consumers to use the Instance property
    static MyCache()
    { }

    /// <summary>
    /// Add an item to the cache with a key and set a absolute expiration on it
    /// </summary>
    public static void Add(string key, object value, int minutes = 90)
    {
        try
        {
            _retryPolicy.ExecuteAction(() => { ACache.Put(key, value, TimeSpan.FromMinutes(minutes)); });
        }
        catch (Exception ex)
        {
            ErrorSignal.FromCurrentContext().Raise(ex);
        }
    }

    /// <summary>
    /// Add the object with the specified key to the cache if it does not exist, or replace the object if it does exist and set a absolute expiration on it
    /// only valid for Azure caching
    /// </summary>
    public static void Put(string key, object value, int minutes = 90)
    {
        try
        {  
            _retryPolicy.ExecuteAction(() => { ACache.Put(key, value, TimeSpan.FromMinutes(minutes)); });
        }
        catch (Exception ex)
        {
            ErrorSignal.FromCurrentContext().Raise(ex);
        }
    }

    /// <summary>
    /// Get a strongly typed item out of cache
    /// </summary>
    public static T Get<T>(string key) where T : class
    {
        try
        {
            object value = null;

            _retryPolicy.ExecuteAction(() => { value = ACache.Get(key); });

            if (value != null) return (T) value;
            return null;
        }
        catch (DataCacheException ex)
        {
            ErrorSignal.FromCurrentContext().Raise(ex);
            return null;
        }
        catch (Exception ex)
        {
            ErrorSignal.FromCurrentContext().Raise(ex);
            return null;
        }
    }
    /// <summary>
    /// Microsoft's suggested method for cleaning up resources such as this in a static class
    /// to ensure connections and other consumed resources are returned to the resource pool
    /// as quickly as possible.
    /// </summary>
    public static void Uninitialize()
    {
        if (_cacheFactory == null) return;
        _cacheFactory.Dispose();
        _cacheFactory = null;
    }
}

注意:这也是使用企业库中的瞬态故障处理块来处理瞬态异常故障。

于 2013-06-24T16:05:14.677 回答