ServiceStack / ASP.NET:所有请求/工作进程都要访问的全局对象?

我正在使用ServiceStack框架开发一个Web服务项目。 我想创建一个全局对象(在我的例子中,是我正在使用的GDS系统的SessionManager对象,它与ASP.NET会话无关),所有传入的请求都可以访问它。

但是,我面临一个问题,即ASP.NET将创建我的应用程序的新实例,从而在其生命周期中创建一个我的SessionManager的新实例几次。 我通过在Global.asax类中的Application_Start和Application_End受保护方法上放置调试行来validation这一点,并意识到Global.asax类在其生命周期中开始和结束多次。 我尝试在静态类中声明我的SessionManager并通过静态构造使用它,但它仍然创建了我的SessionManager的新实例。 不知道为什么。

所以我的问题是如何创建一个可以被所有请求访问的正确的全局(内存)对象?

最初我认为通过使用IoC容器并指定它可以实现单个对象的单例范围,但在ASP.NET世界中似乎不是这种情况。 所以请原谅我在ASP.NET领域的知识,因为我来自前端开发背景。 希望从这个社区的一些专家那里获得这方面的一些知识。 可以提前感谢!

我面临一个问题,即ASP.NET将创建我的应用程序的新实例,从而在其生命周期中创建一个新的SessionManager实例。 我通过在Global.asax类中的Application_Start和Application_End受保护方法上放置调试行来validation这一点,并意识到Global.asax类在其生命周期中开始和结束多次。

IIS应用程序池回收:

你在这里看到的是IIS回收应用程序池。 IIS这样做是为了尝试防止内存泄漏。 您可以按特定间隔配置回收 。

我尝试在静态类中声明我的SessionManager并通过静态构造使用它,但它仍然创建了我的SessionManager的新实例。 不知道为什么。

遗憾的是,静态变量无法在回收中存在,因此如果您的应用程序被回收,则必须创建SessionManager类的新实例。 这意味着您需要处理跨应用程序实例的持久化和恢复状态。

默认情况下,回收过程使用重叠机制,从而在终止旧实例之前启动应用程序的新实例。 这意味着在关闭并启动应用程序实例时,用户无需停机。 不幸的是,这意味着您无法在Application_End保存SessionManager的状态并在新实例中的Application_Start中将其恢复,因为在其他应用程序启动并运行后将调用当前实例的Application_End 。 因此,如果你打算这样做,你需要禁用重叠。 但请记住,如果禁用重叠,那么在回收发生时可能会有很短的停机时间。

本文介绍了回收和注意事项。

我怎么处理这个:

  • 禁用应用程序池回收重叠
  • Application_Start创建一个在应用程序启动时创建的SessionManager的静态实例。
  • Application_End中将SessionManager的状态保存到持久存储,以便在Application_Start初始化时可以恢复到相同的状态。 也许序列化JSON或XML的状态。

最初我认为通过使用IoC容器并指定它可以实现单个对象的单例范围,但在ASP.NET世界中似乎不是这种情况。

一旦解决了回收问题,只要它在全局范围内,就不需要使用IoC来访问ServiceStack中的静态对象。


应用程序重启后维护间隔计划

我有两个维持间隔时间表的解决方案。 解决方案1很简单,不需要外部依赖,但它确实需要保留日期值,但这可能是一个简单的文本文件。 解决方案2是通用的,因为大多数平台都支持它,配置很少。

  1. 我会使用计时器每10分钟运行一次事件,然后记录上次成功检查持久存储中的会话的时间(即文本文件,数据库或外部缓存)。 然后,如果您的应用程序重新启动,它启动时只需确定下一次检查之前应该多长时间。 这将意味着IIS应用程序池回收重新启动不应影响间隔。

    伪代码:

     const int interval = 10; // Run every 10 minutes double timerInverval = 60 * interval; // In seconds // Get last run from peristent storage DateTime? lastRun = GetLastRunTime(); // Substitute with appropriate call from storage // Determine elapsed time if(lastRun.HasValue) { var secondsRemainingUntilNextRun = (lastRun.Value.AddMinutes(interval) - DateTime.Now).TotalSeconds; if(secondsRemainingUntilNextRun <= 0){ // Run immediately, the web application has been down and missed running the SessionManager job SessionManager.CheckSessions(); // Substitute with appropriate call } else { timerInterval = secondsRemainingUntilNextRun; } } // Set a timer to trigger the SessionManager job after timerInterval seconds timer.interval = timerInterval; 
  2. 或者,您可以创建调用Web应用程序并触发此操作的计划任务。 如果独立于Web应用程序触发任务,则在重新启动应用程序时,不必担心维护计划。 我相信Azure具有调度程序服务,或者如果您运行云实例,则可以创建系统调度任务。

您的要求是矛盾的:您需要in-memory存储,并且希望它是可靠且持久的,并且可以在IIS应用程序池中循环使用。 系统内存就不是那么可靠的商店。 如果您需要一些持久性数据,您应该考虑使用为此目的设计的内容:例如数据库甚至硬盘驱动器上的文件。

当然,为了优化性能,您可以使用内存缓存层,以避免每次需要访问信息时都访问持久层。 使用持久性存储的另一个好处是,如果您的应用程序托管在跨多个节点的Webfarm中,则所有这些节点将能够共享相同的数据。

只是不要依赖IIS回收。 无论你在IIS控制台中调整什么选项,AppPool有一天可能只会擦拭你存储在内存中的所有东西,而你无能为力。

ServiceStack支持开箱即用的redis,以及其他几个缓存提供程序:Memcached,Azure,Disk ……因此,选择在何处找到全局会话提供程序仍由您自己决定!

您应该结合缓存机制和单例模式。 因此,您定义了一个可以访问底层缓存提供程序的类,所有请求都只有一个入口点到会话管理器,并使用此缓存提供程序作为您的数据存储库。

它可以在回收,崩溃后存活下来,一旦您需要扩展应用程序,它将使您的生活变得轻松。