【问题标题】:IIS app pool recycle + quartz schedulingIIS应用池回收+石英调度
【发布时间】:2012-06-21 14:40:27
【问题描述】:

我在 IIS 7.5 上运行一个 Web 应用程序,它需要偶尔回收(否则内存使用会失控,我正在调查!)。

当它回收时,它实际上不会运行,直到另一个请求进来,石英不会运行。

有没有办法让IIS在回收应用池后立即自动调出1个工作进程,以确保quartz始终在线?

【问题讨论】:

标签: c# asp.net iis quartz.net


【解决方案1】:

是的!

http://weblogs.asp.net/scottgu/archive/2009/09/15/auto-start-asp-net-applications-vs-2010-and-net-4-0-series.aspx 非常详细,基本上你需要:

  1. 编辑 C:\Windows\System32\inetsrv\config\applicationHost.config 以包括:

    <applicationPools> 
        <add name="MyAppWorkerProcess" managedRuntimeVersion="v4.0" startMode="AlwaysRunning" /> 
    </applicationPools>
    
  2. 声明应该为您的网站运行什么作为“热身”

    <sites> 
        <site name="MySite" id="1"> 
            <application path="/" serviceAutoStartEnabled="true" serviceAutoStartProvider="PreWarmMyCache" />
        </site> 
    </sites>
    <serviceAutoStartProviders> 
        <add name="PreWarmMyCache" type="PreWarmCache, MyAssembly" /> 
    </serviceAutoStartProviders> 
    
  3. 使用您想要的任何“预热”逻辑配置您的应用程序:

    public class PreWarmCache : System.Web.Hosting.IProcessHostPreloadClient {
        public void Preload(string[] parameters) { 
            // Perform initialization and cache loading logic here... 
        } 
    } 
    

注意:如果您只需要存在 w3wp.exe 进程,我相信只有第 1 步是必要的。如果您还需要其他项目(例如将某些内容加载到内存中),那么也将使用第 2 步和第 3 步。

【讨论】:

  • 很抱歉碰到了一个老话题,但是这个热身课应该放在 mvc4 项目的哪个位置?
  • @StephenS。谢谢,但正如Cannot keep alive Web Application on IIS after Recycling or Restarting 所示,它对我不起作用。有什么帮助吗?
  • 对于像我这样尝试使用 notepad++ 或其他编辑器来编辑 applicationHost.config 的其他人,这个答案对 SO 有帮助(基本上,使用记事本):link
【解决方案2】:

从 IIS 8.0 开始,有一个选项可以模拟对根页面的请求,从而进行完整的应用程序初始化:应用程序池高级设置 -> 启用预加载 = true。

当然,startMode 应该是 AlwaysRunning。

有关如何启用此功能的更多详细信息,请参阅here

【讨论】:

    【解决方案3】:

    我解决了这个问题。虽然Stephen's answer 将保持应用程序运行,但在 Spring.Net 环境中,框架不会启动,Quartz 也不会运行。我整理了一个 IProcessHostPreloadClient 的实现,它将向应用程序发出一个真实的请求,以使所有机器运行。这也发on my blog

    public class Preloader : System.Web.Hosting.IProcessHostPreloadClient
    {
        public void Preload(string[] parameters)
        {
            var uris = System.Configuration.ConfigurationManager
                             .AppSettings["AdditionalStartupUris"];
            StartupApplication(AllUris(uris));
        }
    
        public void StartupApplication(IEnumerable<Uri> uris)
        {
            new System.Threading.Thread(o =>
            {
                System.Threading.Thread.Sleep(500);
                foreach (var uri in (IEnumerable<Uri>)o) {
                    var client = new System.Net.WebClient();
                    client.DownloadStringAsync(uris.First());
                }
            }).Start(uris);
        }
    
        public IEnumerable<Uri> AllUris(string userConfiguration)
        {
            if (userConfiguration == null)
                return GuessedUris();
            return AllUris(userConfiguration.Split(' ')).Union(GuessedUris());
        }
    
        private IEnumerable<Uri> GuessedUris()
        {
            string path = System.Web.HttpRuntime.AppDomainAppVirtualPath;
            if (path != null)
                yield return new Uri("http://localhost" + path);
        }
    
        private IEnumerable<Uri> AllUris(params string[] configurationParts)
        {
            return configurationParts
                .Select(p => ParseConfiguration(p))
                .Where(p => p.Item1)
                .Select(p => ToUri(p.Item2))
                .Where(u => u != null);
        }
    
        private Uri ToUri(string value)
        {
            try {
                return new Uri(value);
            }
            catch (UriFormatException) {
                return null;
            }
        }
    
        private Tuple<bool, string> ParseConfiguration(string part)
        {
            return new Tuple<bool, string>(IsRelevant(part), ParsePart(part));
        }
    
        private string ParsePart(string part)
        {
            // We expect IPv4 or MachineName followed by |
            var portions = part.Split('|');
            return portions.Last();
        }
    
        private bool IsRelevant(string part)
        {
            var portions = part.Split('|');
            return
                portions.Count() == 1 ||
                portions[0] == System.Environment.MachineName ||
                HostIpAddresses().Any(a => a == portions[0]);
        }
    
        private IEnumerable<string> HostIpAddresses()
        {
            var adaptors = System.Net.NetworkInformation
                                 .NetworkInterface.GetAllNetworkInterfaces();
            return adaptors
                    .Where(a => a.OperationalStatus == 
                                System.Net.NetworkInformation.OperationalStatus.Up)
                    .SelectMany(a => a.GetIPProperties().UnicastAddresses)
                    .Where(a => a.Address.AddressFamily == 
                                System.Net.Sockets.AddressFamily.InterNetwork)
                    .Select(a => a.Address.ToString());
        }
    }
    

    【讨论】:

      【解决方案4】:

      或者您可以简单地修改 Global.asax 中的“Application_Start”方法以确保 Quortz 正在运行。

      【讨论】:

      • Application_Start 仅在回收或启动后的第一个请求期间调用,这不是所需的行为。
      猜你喜欢
      • 1970-01-01
      • 2012-11-04
      • 2011-09-20
      • 2010-10-10
      • 1970-01-01
      • 1970-01-01
      • 2011-03-10
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多