【问题标题】:ServiceStack/ASP.NET: Global object to be access by all requests/worker processes?ServiceStack/ASP.NET:所有请求/工作进程都可以访问的全局对象?
【发布时间】:2014-04-04 00:09:42
【问题描述】:

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

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

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

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

【问题讨论】:

  • 你对服务器有多少控制权?
  • 对部署端没有太多控制,因为它将是 Windows Azure 网站/共享托管服务器
  • 如果您在云提供商中运行,那么这台机器就是您的。如果您需要一些东西,例如数据库或缓存服务器……只需单击 2 次即可

标签: c# asp.net web-services servicestack


【解决方案1】:

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

IIS 应用程序池回收:

您在这里看到的是 IIS 回收应用程序池。 IIS 这样做是为了尝试防止内存泄漏。 You can configure the recycling 以特定间隔发生。

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

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

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

This article 解释回收和注意事项。

我将如何处理:

  • 禁用应用程序池回收重叠
  • 创建SessionManager 的静态实例,该实例在应用程序启动时在Application_Start 中创建一次。
  • 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 有一个调度服务,或者如果你运行一个云实例,那么你可以创建一个系统调度任务。

【讨论】:

  • 应用程序池回收是有原因的。表现。您不想以长期运行的 w3wp 进程告终。 stackoverflow.com/questions/7171367/…
  • @AdrianSalazar 我从来没有说过不回收应用程序。我只是建议禁用重叠以使状态的保存和恢复更容易。但是,我会更清楚地表明,他们应该继续允许频繁回收。
  • 感谢您的建议,但我不确定我是否应该走这条路,因为我的 SessionManager 计划每隔 10 分钟对池中的所有会话到 GDS 运行扫描/检查刷新它们的有效性。所以我不能为 SessionManager 做一个“暂停”状态。还有其他建议吗?
  • @icube 在不了解 SessionManager 以及为什么需要一个外部进程来刷新所有进程都可以访问的会话有效性的情况下,不可能提供有用的解决方案。如果您能解释发生了什么以及为什么要这样做,那么我可以提供帮助。
  • @Scott SessionManager 的一个目标是保持有效会话的高可用性以供 Web 服务使用。每当请求 Web 服务请求时,SessionManager 都会立即分发会话。这些会话的难点在于每个会话都有一个到期时间,而 SessionManager 的作用之一是刷新/验证它。这是我正在研究的 GDS 设计的。 GDS 文档建议我们使用 SessionManager 来处理传入请求负载过重的会话。
【解决方案2】:

ServiceStack 支持开箱即用的 redis 以及其他几个缓存提供程序:Memcached、Azure、Disk...所以您仍然可以选择在哪里定位您的全局会话提供程序!

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

一旦你必须扩展你的应用程序,它会在回收、崩溃和让你的生活变得轻松。

【讨论】:

    【解决方案3】:

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

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

    不要依赖 IIS 回收。无论您在 IIS 控制台中调整什么选项,AppPool 总有一天可能会在擦除您存储在内存中的所有内容时死掉,而您对此无能为力。

    【讨论】:

    • 感谢您的回答以及有关 IIS 回收的信息。我不知道 ASP.NET 中存在这样的“功能/问题”。我会尝试重新考虑我的实现...谢谢!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-08-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多