【问题标题】:How to manage cache in ASP.NET WebApi2?如何在 ASP.NET WebApi2 中管理缓存?
【发布时间】:2017-01-24 07:57:57
【问题描述】:

我已经使用 WebAPI2 实现了 REST 服务,该服务用于管理由访问服务的不同客户端创建和加入的不同会话。

会话包含有关应用程序功能访问的信息以及已加入同一会话的参与者的信息。

每个客户端每秒都从服务器获取会话信息和访问列表以进行同步。根据访问权限的变化,客户端功能也会发生变化(启用/禁用)。

我正在使用 MemoryCache 类将会话信息存储在 WebAPI 服务中,如下所示。

public static class SessionManager{
private static object objForLock = new object();
public static List<Session> SessionCollection
{
    get
    {
        lock (objForLock)
        {
            MemoryCache memoryCache = MemoryCache.Default;
            return memoryCache.Get("SessionCollection") as List<Session>;
            // return HttpContext.Current.Application["SessionCollection"] as List<Session>;
        }
    }
    set
    {
        lock (objForLock)
        {
            MemoryCache memoryCache = MemoryCache.Default;
            memoryCache.Add("SessionCollection", value, DateTimeOffset.UtcNow.AddHours(5));
            //HttpContext.Current.Application["SessionCollection"] = value;  
        }
    }
}

}

我的问题是关于缓存的不一致行为。

当客户端发送同步调用时,它会给出不一致的结果。对于某些请求,客户端会获得正确的数据,而对于某些请求,客户端会在某些请求后获得空数据替代。

我已添加调试器并监视对象是否为空结果,然后 "memoryCache.Get("SessionCollection")" 也为空。经过一些连续的请求后,它将再次正确。我不明白为什么这个对象不是持久的。

替代方案,我也尝试过 "HttpContext.Current.Application["SessionCollection"]",但同样的问题也存在。

我读过“应用程序池回收”,它会在特定时间后回收所有缓存。如果我的缓存对象被app pool recycle回收了,那我怎样才能再次得到这个对象呢?

请一些人可以帮助我摆脱这个问题。提前致谢。

【问题讨论】:

  • 只是想再次检查您的服务器时间是否也设置为 UTC。超时值是否可能在您预期之前实际达到?
  • 顺便说一句,MemoryCache 是线程安全的,因此不需要锁定
  • 您使用哪个 .NET 版本?
  • @RAM 我正在​​使用 .Net 框架 4.0。 .Net framework 4.5 能帮我解决这个问题吗?
  • 我刚刚用更多信息更新了我的答案。另请检查您是否在 IIS 中将应用程序部署为多个工作进程。但是无论如何都不建议在本地存储Cache

标签: c# caching iis asp.net-web-api2


【解决方案1】:

您应该将客户特定信息存储在Session 而不是CacheCache 应该用于整个应用程序(共享)

但是,不建议这样做,因为 Web api 在构建时考虑到了 RESTful,并且 RESTful 服务应该是无状态的(API 不缓存状态)。无状态应用程序有很多好处:

  • 减少内存使用
  • 更好的可扩展性:您的应用程序可以更好地扩展。想象一下如果同时存储数百万客户的信息会发生什么。
  • 在负载平衡场景中表现更好:每个服务器都可以处理每个客户端而不会丢失状态。
  • 会话过期问题。

如果您想存储客户端状态,无论如何都可以这样做。请尝试以下帖子中的建议:ASP.NET Web API session or something?

一般来说,在 Web 服务器上本地缓存状态很糟糕(Session 和本地 MemoryCache)。缓存丢失的原因有很多:

  • 应用程序池回收。
  • 负载均衡环境
  • IIS 中的多个工作进程

关于您的要求:

每个客户端从服务器获取会话信息和访问列表 每秒同步的目的。根据访问权限改变, 客户端功能将更改(启用/禁用)。

我不确定您是否想在客户端发送同步调用时立即使用新的访问列表更新其他客户端。如果是这样,SignalR 会是更好的选择。

否则,您可以将更新的访问列表存储在某处(共享缓存甚至数据库中),并在其他客户端重新连接另一个请求时更新它们。

【讨论】:

    【解决方案2】:

    @ScottHanselman 提到了 .NET 4 here 中的一个错误。我希望此修复对您有所帮助:

    临时修复

    在禁用的执行上下文流下创建内存缓存实例

    using (ExecutionContext.SuppressFlow())     {
              // Create memory cache instance under disabled execution context flow
             return new YourCacheThing.GeneralMemoryCache(…);
    }
    

    修补程序http://support.microsoft.com/kb/2828843,您可以在这里申请:https://support.microsoft.com/contactus/emailcontact.aspx?scid=sw;%5BLN%5D;1422

    【讨论】:

      【解决方案3】:

      请注意,MemoryCache 会将数据保存在单个服务器的内存中。因此,如果您有多个 Web 服务器(在负载平衡器之前),则该缓存将无法用于其他服务器。您还可以使用缓存名称 - “SessionCollection”。该数据将共享给所有客户端。如果您需要将数据存储在每个客户端唯一的缓存中,则需要向客户端返回一个令牌(guid),并使用该令牌在后续请求中获取/更新缓存中的数据。

      尝试引入类级别变量。因此,您的代码将如下所示。 (为了清楚起见,删除了一些代码)

      private readonly MemoryCache _memCache = MemoryCache.Default;
      

      ....

      return _memCache.Get("SessionCollection") as List<Session>;
      

      ...

      _memCache .Add("SessionCollection", value, DateTimeOffset.UtcNow.AddHours(5));
      

      【讨论】:

      • @alltej感谢您的回复。我正在使用单个服务器以及为所有客户端共享“会话集合”数据。对于这种数据不一致还有什么建议吗?
      • @NileshWagh 更新了我的答案。你能检查一下吗?
      • 嗨@alltej,我已经根据您编辑的答案添加了更改,但我仍然面临同样的问题。
      • @NileshWagh 你能在 _memCache 上做一个 Set 而不是 Add 吗?此链接可能对您有所帮助:stackoverflow.com/questions/16018009/…
      猜你喜欢
      • 1970-01-01
      • 2015-01-15
      • 2019-02-09
      • 2015-09-04
      • 2021-02-16
      • 1970-01-01
      • 2011-07-16
      • 1970-01-01
      • 2014-04-15
      相关资源
      最近更新 更多