【问题标题】:What is causing my httpcontext.cache to be removed in my c# MVC web application?是什么导致我的 httpcontext.cache 在我的 c#MVC Web 应用程序中被删除?
【发布时间】:2015-12-19 22:09:18
【问题描述】:

我们有一个 Web 应用程序,它从 SOAP Web 服务中检索大量数据,大约需要 4 到 5 分钟。 为了确保用户不会被此困扰,数据缓存如下:

//using Nlog to log caching behaviour
private Logger log = LogManager.GetCurrentClassLogger();
//a public actionresult to get the summaries with an ajax call or site24x7 service
public ActionResult GetSummariesAsync()
{
  log.Info(String.Format("GetSummariesAsync called"));
  List<ProjectDataSummary> summaries = GetAllSummaries();
  log.Info(String.Format("{0} summaries found", summaries.Count));
  List<ProjectDataSummary> cache = HttpContext.Cache["SummariesCache"] as List<ProjectDataSummary>;
  log.Info(String.Format("{0} in cache", cache.Count));

  return Json(summaries, JsonRequestBehavior.AllowGet);
}
private List<ProjectDataSummary> GetAllSummaries()
{
  List<ProjectDataSummary> summaries = new List<ProjectDataSummary>();
  //use setting in web.config if we want to force no cache, but set to false in released version
  if (ConfigurationManager.AppSettings["NoCache"] == "true")
  {
    summaries = _service.GetAllSummaries();
  }
  else
  {
    //get summaries from cache if available
    summaries = HttpContext.Cache["SummariesCache"] as List<ProjectDataSummary>;
    if (summaries == null)
    {
      //cache empty, retrieve values
      summaries = _service.GetAllSummaries();
      //cache it
      HttpContext.Cache.Add("SummariesCache", summaries, null, new DateTime(DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day, 23, 59, 59), Cache.NoSlidingExpiration, CacheItemPriority.NotRemovable, new CacheItemRemovedCallback(this.cacheCallback));
    }
  }
  return summaries;
}
private void cacheCallback(String K, Object v, CacheItemRemovedReason r)
{
  CacheItemRemovedReason reason = r;
  log.Info("Cache expired, reason: {0}", r.ToString());
}

我有一个来自www.site24x7.com 的服务每小时都会创建一次缓存,所以理论上它应该在早上 0:00 之后点击它并让它创建另一个缓存。但由于某种原因,缓存被删除了,网站每天会创建几次新的缓存,加载时间也很长。

这是我的 Nlog 日志中的一部分:

2015-09-22 18:31:07.8746 GetSummariesAsync Info GetSummariesAsync called 
2015-09-22 18:31:07.8901 GetSummariesAsync Info 263 summaries 
2015-09-22 18:31:07.8901 GetSummariesAsync Info 263 in cache 
2015-09-22 18:52:15.9684 cacheCallback Info Cache expired, reason: Removed 
2015-09-22 19:02:06.9839 cacheCallback Info Cache expired, reason: Removed 
2015-09-22 19:23:08.3590 cacheCallback Info Cache expired, reason: Removed 
2015-09-22 19:31:42.7182 GetSummariesAsync Info GetSummariesAsync called 
2015-09-22 19:32:15.0776 GetSummariesAsync Info GetSummariesAsync called 
2015-09-22 19:32:46.2495 GetSummariesAsync Info GetSummariesAsync called 
2015-09-22 19:33:01.8276 GetSummariesAsync Info GetSummariesAsync called 
2015-09-22 19:33:18.3746 GetSummariesAsync Info GetSummariesAsync called 
2015-09-22 19:33:34.8589 GetSummariesAsync Info GetSummariesAsync called 
2015-09-22 19:34:10.7182 GetSummariesAsync Info GetSummariesAsync called 
2015-09-22 19:34:15.4215 GetSummariesAsync Info 263 summaries 
2015-09-22 19:34:15.4215 GetSummariesAsync Info 263 in cache 
2015-09-22 19:35:07.3121 GetSummariesAsync Info 263 summaries 
2015-09-22 19:35:07.3121 GetSummariesAsync Info 263 in cache 
2015-09-22 19:36:07.9213 GetSummariesAsync Info 263 summaries 
2015-09-22 19:36:07.9213 GetSummariesAsync Info 263 in cache 
2015-09-22 19:36:19.5309 GetSummariesAsync Info 263 summaries 
2015-09-22 19:36:19.5309 GetSummariesAsync Info 263 in cache 
2015-09-22 19:36:36.3588 GetSummariesAsync Info 263 summaries 
2015-09-22 19:36:36.3588 GetSummariesAsync Info 263 in cache 
2015-09-22 19:37:07.9996 GetSummariesAsync Info 263 summaries 
2015-09-22 19:37:07.9996 GetSummariesAsync Info 263 in cache 
2015-09-22 19:37:33.7183 GetSummariesAsync Info 263 summaries 
2015-09-22 19:37:33.7183 GetSummariesAsync Info 263 in cache 
2015-09-22 19:37:59.8747 GetSummariesAsync Info GetSummariesAsync called 
2015-09-22 19:37:59.8747 GetSummariesAsync Info 263 summaries 
2015-09-22 19:37:59.8747 GetSummariesAsync Info 263 in cache 
2015-09-22 19:44:00.2496 cacheCallback Info Cache expired, reason: Removed 
2015-09-22 19:54:25.2183 cacheCallback Info Cache expired, reason: Removed 
2015-09-22 20:34:55.3589 GetSummariesAsync Info GetSummariesAsync called 
2015-09-22 20:34:55.3745 GetSummariesAsync Info 263 summaries 
2015-09-22 20:34:55.3745 GetSummariesAsync Info 263 in cache 
2015-09-22 20:44:32.5153 cacheCallback Info Cache expired, reason: Removed 
2015-09-22 20:56:38.8746 cacheCallback Info Cache expired, reason: Removed

编辑

我已经实现了 NightOwl888 给出的答案,但无济于事,缓存不断被删除。

这是我当前 Nlog 文件中的一部分:

2015-09-27 20:05:29.9682 Controllers.HomeController.GetSummariesAsync Info 263 summaries found 
2015-09-27 20:05:29.9682 Controllers.HomeController.GetSummariesAsync Info 263 in cache found 
2015-09-27 20:23:51.5464 Controllers.HomeController.cacheCallback Info Cache expired, reason: Removed 
2015-09-27 20:23:51.5464 Controllers.HomeController.cacheCallback Info Cache expired, reason: Removed 
2015-09-27 20:23:51.5464 Controllers.HomeController.cacheCallback Info Cache expired, reason: Removed 
2015-09-27 20:25:48.1714 Models.CachingBase`1.GetItemsCache Info Cache fileEntries created 
2015-09-27 20:25:48.1714 Models.CachingBase`1.GetItemsCache Info Cache idCertificaat created 
2015-09-27 20:26:46.3588 Controllers.HomeController.Index Info No cache found, Home/Index needs to create the cash Async 
2015-09-27 20:26:46.7963 Controllers.HomeController.GetSummariesAsync Info GetSummariesAsync triggered by User-Agent: Mozilla/5.0 (Windows NT 6.3; WOW64; rv:40.0) Gecko/20100101 Firefox/40.0 vanuit Host: 88.159.95.251 
2015-09-27 20:27:26.9682 Models.CachingBase`1.GetItemsCache Info Cache GebouwProjectenSummaries created 
2015-09-27 20:27:26.9682 Controllers.HomeController.GetSummariesAsync Info 263 summaries found 
2015-09-27 20:27:26.9682 Controllers.HomeController.GetSummariesAsync Info 263 in cache found 
2015-09-27 20:33:48.7340 Controllers.HomeController.cacheCallback Info Cache expired, reason: Removed 
2015-09-27 20:33:48.7340 Controllers.HomeController.cacheCallback Info Cache expired, reason: Removed 
2015-09-27 20:33:48.7340 Controllers.HomeController.cacheCallback Info Cache expired, reason: Removed 
2015-09-27 20:38:21.0151 Models.CachingBase`1.GetItemsCache Info Cache fileEntries created 
2015-09-27 20:38:21.0151 Models.CachingBase`1.GetItemsCache Info Cache idCertificaat created 
2015-09-27 20:38:21.0620 Controllers.HomeController.Index Info No cache found, Home/Index needs to create the cash Async 
2015-09-27 20:43:41.0620 Controllers.HomeController.Index Info No cache found, Home/Index needs to create the cash Async 
2015-09-27 20:46:29.0620 Models.CachingBase`1.GetItemsCache Info Cache GebouwProjectenSummaries created 
2015-09-27 20:57:21.5776 Controllers.HomeController.cacheCallback Info Cache expired, reason: Removed 
2015-09-27 20:57:21.5776 Controllers.HomeController.cacheCallback Info Cache expired, reason: Removed 
2015-09-27 20:57:21.5776 Controllers.HomeController.cacheCallback Info Cache expired, reason: Removed 
2015-09-27 20:58:23.9553 Models.CachingBase`1.GetItemsCache Info Cache fileEntries created 
2015-09-27 20:58:23.9553 Models.CachingBase`1.GetItemsCache Info Cache idCertificaat created 
2015-09-27 20:58:23.9839 Controllers.HomeController.Index Info No cache found, Home/Index needs to create the cash Async 
2015-09-27 21:04:24.5151 Controllers.HomeController.cacheCallback Info Cache expired, reason: Removed 
2015-09-27 21:04:24.5151 Controllers.HomeController.cacheCallback Info Cache expired, reason: Removed 
2015-09-27 21:05:31.8277 Models.CachingBase`1.GetItemsCache Info Cache fileEntries created 
2015-09-27 21:05:31.8277 Models.CachingBase`1.GetItemsCache Info Cache idCertificaat created 
2015-09-27 21:05:31.8432 Controllers.HomeController.GetSummariesAsync Info GetSummariesAsync triggered by User-Agent: Site24x7 vanuit Host: 72.5.230.111 
2015-09-27 21:06:03.5307 Controllers.HomeController.GetSummariesAsync Info GetSummariesAsync triggered by User-Agent: Site24x7 vanuit Host: 72.5.230.111 
2015-09-27 21:06:36.3433 Controllers.HomeController.GetSummariesAsync Info GetSummariesAsync triggered by User-Agent: Site24x7 vanuit Host: 120.138.27.125 
2015-09-27 21:06:40.2651 Controllers.HomeController.GetSummariesAsync Info GetSummariesAsync triggered by User-Agent: Site24x7 vanuit Host: 119.81.237.98 
2015-09-27 21:07:08.6401 Controllers.HomeController.GetSummariesAsync Info GetSummariesAsync triggered by User-Agent: Site24x7 vanuit Host: 120.138.27.125 
2015-09-27 21:07:11.4057 Models.CachingBase`1.GetItemsCache Info Cache GebouwProjectenSummaries created 
2015-09-27 21:07:11.4057 Controllers.HomeController.GetSummariesAsync Info 263 summaries found 
2015-09-27 21:07:11.4057 Controllers.HomeController.GetSummariesAsync Info 263 summaries found 
2015-09-27 21:07:11.4057 Controllers.HomeController.GetSummariesAsync Info 263 in cache found 
2015-09-27 21:07:11.4057 Controllers.HomeController.GetSummariesAsync Info 263 in cache found 
2015-09-27 21:07:11.4214 Controllers.HomeController.GetSummariesAsync Info 263 summaries found 
2015-09-27 21:07:11.4214 Controllers.HomeController.GetSummariesAsync Info 263 in cache found 
2015-09-27 21:07:11.4214 Controllers.HomeController.GetSummariesAsync Info 263 summaries found 
2015-09-27 21:07:11.4214 Controllers.HomeController.GetSummariesAsync Info 263 in cache found 
2015-09-27 21:07:11.4214 Controllers.HomeController.GetSummariesAsync Info 263 summaries found 
2015-09-27 21:07:11.4214 Controllers.HomeController.GetSummariesAsync Info 263 in cache found 
2015-09-27 21:07:12.5620 Controllers.HomeController.GetSummariesAsync Info GetSummariesAsync triggered by User-Agent: Site24x7 vanuit Host: 119.81.237.98 
2015-09-27 21:07:12.5620 Controllers.HomeController.GetSummariesAsync Info 263 summaries found 
2015-09-27 21:07:12.5620 Controllers.HomeController.GetSummariesAsync Info 263 in cache found 
2015-09-27 21:15:32.4370 Controllers.HomeController.cacheCallback Info Cache expired, reason: Removed 
2015-09-27 21:15:32.4370 Controllers.HomeController.cacheCallback Info Cache expired, reason: Removed 
2015-09-27 21:15:32.4370 Controllers.HomeController.cacheCallback Info Cache expired, reason: Removed 

可以看到,Site24x7 有时会连续多次调用 GetSummariesAsync 函数。

而且用户也会(尽管很少)遇到问题。

从创建现金到取出现金之间的时间可以短至 8 分钟。

我现在禁用了我的 site24x7 检查,看看这是否是罪魁祸首。

【问题讨论】:

  • 可能是web应用空闲了20分钟,进程掉线了。您能否使用monitis 等监控服务或hangfire 等后台作业定期每隔5 到10 分钟ping 一次应用程序,看看是否解决了问题?

标签: c# asp.net-mvc caching site24x7


【解决方案1】:

我不知道这是否是您的问题的根源,但我发现您的竞争条件是由多个线程调用您的 GetAllSummaries 方法引起的。

当多个线程正在填充缓存时,您可能会收到错误的日志条目(由于冲突导致多余的条目被删除)并且用户只会遇到问题,因为多个线程在填充缓存之前竞争系统资源.

首先看一下Cache.Add方法的文档:

如果具有相同键参数的项目已存储在缓存中,则调用此方法将失败。要使用相同的键参数覆盖现有的缓存项,请使用Insert 方法。

还有more importantly

如果您使用 Add 方法并且缓存中已经存在同名的项目,则该方法不会替换该项目并且不会引发异常

(强调)

此外,您的缓存检查代码不是线程安全的。多个线程可以运行GetAllSummaries 方法,因为缓存在第一次调用返回之前不会有值。而且由于 Add 方法不会因为重复而引发异常,所以这种努力只是浪费了。

您可以通过以下方式解决此问题:

  1. 创建一个对象来包装缓存,以便对缓存进行线程安全的多次调用。
  2. 使用double-checked locking pattern 确保只允许一个线程通过以获取数据。
  3. 使用缓存包装器的静态实例,确保所有线程使用相同的线程锁定代码。

SummariesCache

public sealed class SummariesCache
{
    private ReaderWriterLockSlim synclock = 
        new ReaderWriterLockSlim(LockRecursionPolicy.NoRecursion);

    public List<ProjectSummaryData> GetSummaries(
        ISummariesService service, 
        ref CacheItemRemovedCallback onRemoveCallback)
    {
        List<ProjectDataSummary> summaries;
        string key = "SummariesCache";
        bool success;

        synclock.EnterReadLock();
        try
        {
            success = TryGetCacheValue(key, out summaries);
        }
        finally
        {
            synclock.ExitReadLock();
        }

        if (!success)
        {
            synclock.EnterWriteLock();
            try
            {
                if (!TryGetCacheValue(key, out summaries))
                {
                    //cache empty, retrieve values
                    summaries = service.GetAllSummaries();

                    // load the cache (using Insert)
                    HttpContext.Current.Cache.Insert(
                        key, 
                        summaries, 
                        null, 
                        new DateTime(DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day, 23, 59, 59), 
                        Cache.NoSlidingExpiration, 
                        CacheItemPriority.NotRemovable, 
                        onRemoveCallback
                    );
                }
            }
            finally
            {
                synclock.ExitWriteLock();
            }
        }

        return summaries;
    }

    private bool TryGetCacheValue(string key, out List<ProjectSummaryData> value)
    {
        value = HttpContext.Current.Cache["key"] as List<ProjectDataSummary>;
        if (value != null)
        {
            return true;
        }
        return false;
    }
}

用法

// Use a static instance of the cache to ensure all threads use it.
private static _summariesCache = new SummariesCache();

private List<ProjectDataSummary> GetAllSummaries()
{
    List<ProjectDataSummary> summaries = new List<ProjectDataSummary>();
    //use setting in web.config if we want to force no cache, but set to false in released version
    if (ConfigurationManager.AppSettings["NoCache"] == "true")
    {
        summaries = _service.GetAllSummaries();
    }
    else
    {
        summaries = _summariesCache.GetSummaries(_service, new CacheItemRemovedCallback(cacheCallback));
    }
    return summaries;
}

private void cacheCallback(String K, Object v, CacheItemRemovedReason r)
{
    CacheItemRemovedReason reason = r;
    log.Info("Cache expired, reason: {0}", r.ToString());
}

注意:大部分代码是从Micro-Caching in .NET 借来的。如果您愿意,可以使用该解决方案。

【讨论】:

  • 我已经在理论上很好地实现了这一点。现在我们等待。现在谢谢,如果这确实解决了我们的问题,将标记为答案。
  • +1 以更好地实现缓存,尽管您的回答并没有解决我的问题。我实际上有 3 个缓存,我在这个项目中使用了你的代码(以抽象类为基础,所以我没有重复的代码)
  • 很抱歉,它没有解决您的所有问题。您可以尝试切换到 .NET 4.0 中添加的 System.Runtime.Caching.MemoryCacheSystem.Web.Caching.Cache 早在 .NET 1.1 中就被添加了。请注意,Microsoft 还在 ASP.NET 5 中添加了 2 个新的缓存提供程序(并摆脱了这两个)。但无论您选择哪个选项,请确保保持双重检查锁定模式以确保线程安全。
【解决方案2】:

HttpContext.Cache 仅在应用程序的生命周期内可用,并且该生命周期与 IIS 应用程序池相关联。当应用程序池回收或终止时,缓存将被清除。不幸的是,您无法控制 IIS 何时执行此操作。在我的脑海中,我可以看到 2 个选项或(可能两者兼而有之)。

  1. 将缓存存储在本地数据库中。仍然比内存缓存慢,但可能比网络服务快。
  2. 在应用程序启动时重新加载缓存(在 Global.asax 文件的 Application_Start() 函数内),而不是定期 ping 或除定期 ping 之外(取决于您的要求)。

【讨论】:

    【解决方案3】:

    抱歉,如果这个答案没有帮助,但我之前看到过类似的问题,所以我想我会发布。

    在记录器(在本例中为 nlog)实际写入应用程序目录(尤其是 bin 文件夹)之前,我已经看到了一个问题。通过这样做,它实际上会触发网站重新加载其域逻辑,从而导致会话和缓存数据失效。

    再次抱歉,如果这对您没有帮助,但我认为值得一提。

    【讨论】:

    • Nlog 存储到 App_data 文件夹中,在我使用 Nlog 之前,应用程序就遇到了这个问题。我已经在使用 Elmah 错误日志记录,而且还使用了 App_Data 文件夹,并且该应用程序已经运行良好一段时间了。
    猜你喜欢
    • 2015-03-31
    • 2016-10-26
    • 2012-07-13
    • 1970-01-01
    • 2014-06-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多