asp.net 定时任务和IIS程序池的配置

ASP.NET 项目中3个定时任务,其中有一个任务经常莫名奇妙的没有运行,其他两个任务运行正常。
定时任务业务逻辑是:每日在固定时间范围内(例如:22:00~23:00)进行考勤统计,统计后将结果发送微信消息给管理员。
没有运行有两种情况:1.完全没有Timer的时间处理程序(日志中没有记录下面代码中的“开始生成考勤策略”字样)。2.运行了统计逻辑但没有发送微信消息(未收到微信消息,也没有微信发送失败的相关错误日志)。主要代码如下

//程序启动
 protected void Application_Start()
        {
            AreaRegistration.RegisterAllAreas();
            GlobalConfiguration.Configure(WebApiConfig.Register);
            //WebApiConfig.Register(GlobalConfiguration.Configuration);
            FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
            RouteConfig.RegisterRoutes(RouteTable.Routes);
            BundleConfig.RegisterBundles(BundleTable.Bundles);

            try
            {
                LogFile.WriteError(DateTime.Now + ":服务启动。");
                //== 考勤策略生成服务
                var checkWorkInterval = double.Parse((CachedConfigContext.Current.CheckWorkConfig.Interval * 1000).ToString());
                Timer checkWorTime = new Timer(checkWorkInterval);
                checkWorTime.Elapsed += new System.Timers.ElapsedEventHandler(CheckWorkEvent);
                checkWorTime.AutoReset = true;
                checkWorTime.Enabled = Convert.ToBoolean(CachedConfigContext.Current.CheckWorkConfig.MasterSwitch);
            }
            catch (Exception ex)
            {
                LogFile.WriteError(DateTime.Now + ";服务发生错误:" + ex.ToString());
            }
        }
        //IIS休眠或结束时重新唤醒
        protected void Application_End()
        {
            /*防止定时器失效*/
            LogFile.WriteError("触发Application_End方法");
            System.Threading.Thread.Sleep(5000);
            string strUrl = "http://" + Application["WebApiUri_Authority"].ToString() + "/api/Msg/Post";

            string strRec = HttpClientHelper.PostData("", strUrl, "", "", HttpClientHelper.PostContentType.JSON);
        }
        /// <summary>
        /// 每日考勤策略生成事件
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void CheckWorkEvent(object sender, ElapsedEventArgs e)
        {
            var time = (Timer)sender;
            DateTime nowDate = DateTime.Now;//当前时间
            try
            {
                DateTime startDate =CachedConfigContext.Current.CheckWorkConfig.StartTime;//22:00
                DateTime endDate =CachedConfigContext.Current.CheckWorkConfig.EndTime;//23:00
                if (startDate <= nowDate && nowDate <= endDate)
                {
                    time.Stop();
                    LogFile.WriteError(DateTime.Now + ":开始生成考勤策略");
                    //生成考勤策略
                    var count = checkworkBll.CheckWorkCreate();//具体的业务逻辑
                    LogFile.WriteError(DateTime.Now + ":共生成" + count + "条考勤策略");
                    LogFile.WriteError(DateTime.Now + ":生成考勤策略结束");
                    time.Start();
                }//判断是否在固定时间范围内
            }
            catch (Exception ex)
            {
                LogFile.WriteError(DateTime.Now + ":生成策略错误:" + ex.ToString());
                time.Start();
            }
        }

每次任务停止后,我尝试重启程序就恢复正常,两天后又停止了,因为以前没有出现过该问题,于是我回想了最近做过的改动,曾经配置过IIS程序池将启动模式从“OnDemand”改为“AlwaysRunning”,同时将进程模型中的闲置时间20分钟改为0,再没有任何头绪的情况下,我尝试改回原先的配置后,竟然正常了。

img

img

我的问题是:IIS程序池的配置为什么会影响?是什么原理?请哪位有经验的朋友帮助解惑下,谢谢!
程序运行的环境是:操作系统windows server 2012 iis 8.0 .net4.5

基于Monster 组和GPT的调写:
两个影响因素:System.Timers.Timer 类和 IIS 应用程序池

  • 你的定时任务的代码实现中使用了 System.Timers.Timer 类。这个类在 ASP.NET 应用程序中使用时,需要特别小心,因为它的工作方式与 ASP.NET 运行的线程池机制不兼容,可能会导致定时任务不准确或停止工作。
  • 另外,在 IIS 中对应用程序池的设置进行了更改,将启动模式从 "OnDemand" 更改为 "AlwaysRunning",并将进程模型中的空闲时间从 20 分钟更改为 0。这些更改可以影响应用程序池的行为,导致它更积极地保持应用程序处于运行状态,从而确保您的定时任务正常工作。
  • 具体地说,如果应用程序池的启动模式为 "AlwaysRunning",则 IIS 将在应用程序池的第一个请求到达之前启动应用程序,并将其保持运行状态,直到应用程序池被停止或 IIS 服务关闭。这有助于确保您的定时任务能够在预期的时间内启动和运行。
  • 另一方面,如果进程模型中的空闲时间为 0,则 IIS 将不会在应用程序空闲时回收应用程序池中的工作进程。这意味着你的定时任务将始终处于运行状态,而不会被 IIS 中止或回收。
  • 总之,你的定时任务可能受到 System.Timers.Timer 类和 IIS 应用程序池设置之间的交互影响。为了确保你的定时任务始终正常工作,建议你使用更可靠的定时任务实现方式,例如使用 Windows 服务或使用基于 Quartz.NET 等第三方库的任务调度程序。

该回答引用ChatGPT

根据你的描述,可能是由于IIS的应用程序池配置发生了变化导致了定时任务的异常。具体来说,如果应用程序池的启动模式被设置为"OnDemand",那么当没有请求进入应用程序池时,IIS将停止应用程序池以节省资源。这将导致定时任务无法按预期执行,因为计时器事件在没有请求的情况下不会被触发。将启动模式更改为"AlwaysRunning"可以解决这个问题。

此外,将应用程序池中的空闲时间设置为0也可以解决此问题。因为在这种情况下,IIS不会在应用程序池处于空闲状态时停止它。这样,即使没有请求进入应用程序池,定时任务仍将按预期执行。

你把运行日志发给我看一下

参考GPT和自己的思路,IIS程序池的配置可以影响ASP.NET项目的定时任务的正常运行。具体原因是因为IIS默认会对空闲的应用程序池进行回收,当应用程序池被回收后,其中的所有应用程序都将被停止,包括ASP.NET项目中的定时任务。因此,当程序池被回收后,部分或全部定时任务可能会停止运行。

在ASP.NET项目中,可以通过以下两种方式来解决定时任务被停止运行的问题:

1.调整IIS程序池的配置,将其设置为始终运行(Always Running),这样可以避免应用程序池被回收的问题。在IIS 8.0中,可以在“高级设置”中找到“始终运行”选项,并将其设置为“True”。
2.通过使用一些第三方工具或自己编写代码,定时发送请求以保持ASP.NET应用程序处于活动状态,这样可以避免应用程序池被回收的问题。例如,可以使用Windows服务或其他定时任务来发送请求,或者使用一些开源的库,如ASP.NET SignalR。
在您的情况下,将IIS程序池的配置从“OnDemand”改为“AlwaysRunning”,并将进程模型中的闲置时间20分钟改为0后,可以解决定时任务停止运行的问题。这是因为这些配置调整使应用程序池始终保持活动状态,不会被回收,从而保证了ASP.NET项目中的定时任务的正常运行。

这个问题可能是由多种原因导致的,例如:

定时任务设置有误:请检查定时任务的设置是否正确,特别是时间范围是否正确。如果时间范围设置有误,那么在非指定时间段内定时任务将不会运行。

代码实现有误:请检查定时任务的代码实现是否正确,是否存在逻辑或语法错误。特别是需要检查是否存在异常或错误未被捕获和处理的情况,这可能会导致定时任务未能正常执行。

环境配置问题:请检查定时任务运行的环境配置是否正确,包括操作系统、.NET Framework版本等。如果环境配置有误,可能会导致定时任务未能正常运行。

微信消息发送失败:请检查微信消息发送的相关配置是否正确,例如微信接口地址、访问令牌等。如果微信消息发送失败,可能会导致定时任务未能正常发送消息。
建议您针对上述原因进行排查,先检查定时任务的设置和代码实现是否正确,然后检查环境配置是否正确,并最后排查微信消息发送的相关配置是否正确。如果问题仍然存在,请提供更多的信息,例如定时任务的代码实现和定时任务的配置信息,以便更好地定位和解决问题。

以下答案由GPT-3.5大模型与博主波罗歌共同编写:
一、定时任务问题

首先,从代码中可以看出定时任务是通过 Timer 类实现的,请确保 Timer 实例被妥善地释放,否则将会导致 Timer 永远不会被回收,造成内存泄露。可以在 Stop 方法之后加上控制语句,保证资源正确释放。

其次,可以考虑在定时任务每次执行完成后,将其所使用到的资源都释放掉,包括连接对象、文件流或者其他资源,防止资源占用过多导致其他任务运行失败。根据你的代码片段,可以在 CheckWorkCreate 方法执行完毕后,加上对该方法中所使用到的资源进行释放操作。

最后,关于定时任务遇到莫名其妙问题的解决方法。可以通过输出日志等调试信息找到问题出现的位置。同时,在代码中加上捕捉异常的代码逻辑,在出现异常后,将异常信息写入日志中,以便于查询问题所在。

二、IIS程序池配置问题

对于 IIS 程序池配置对定时任务运行的影响,可能是因为程序在 IIS 中运行,如果采用默认的配置,程序池空闲时会被系统自动回收,导致当定时任务执行时,程序内部 Timer 对象并没有被回收,从而造成程序无法正常运行。而将程序池的取消空闲回收配置后,空闲时仍然驻留在内存中,这个时候程序池就不会回收程序,内部的 Timer 对象也就不会被回收,从而保证了定时任务能够顺利地执行。

在实际部署过程中需要注意程序池配置的修改,如果修改了程序池配置,必须重新启动应用程序池以使配置生效,可以通过修改 web.config 文件或者 IIS 管理端口进行配置。以下是可以实现该功能的 web.config 配置文件的示例代码:

<applicationPools>
  <add name="YourApplicationPoolName" managedRuntimeVersion="v4.0" startMode="AlwaysRunning" />
</applicationPools>

<system.applicationHost>
  <sites>
    <site name="YourSiteName" id="1" serverAutoStart="true">
      <application path="/" applicationPool="YourApplicationPoolName" preloadEnabled="true" />
    </site>
  </sites>
</system.applicationHost>

以上代码将程序池的启动模式设置为 AlwaysRunning,并且将网站的预加载开关打开,即在 IIS 启动时预先加载网站,确保了程序池不会在 IIS 闲置时被回收。但请注意,在将站点开启预加载后会占用较多的服务器资源,因此需要为程序池分配足够的服务器资源。

最后,建议将日志功能完善,对程序的运行状态进行监控,如果出现问题时可以及时根据日志进行定位和解决。
如果我的回答解决了您的问题,请采纳!

该回答引用自ChatGPT:
在ASP.NET项目中,定时任务通常使用定时器(Timer)来实现,这些定时器需要在后台持续运行,以便在指定的时间间隔内执行某个任务。在IIS应用程序池的配置中,有一个选项可以控制应用程序池的闲置时间。如果应用程序池在闲置时间内没有任何活动,它将被自动回收,以释放系统资源。这意味着,如果你的定时器处于闲置状态,它可能会被自动回收,从而导致你的定时任务不能按照预期执行。

在你的情况下,将应用程序池的闲置时间设置为0,相当于告诉IIS不要回收应用程序池。这可以确保你的定时器一直在后台运行,并按照预期执行任务。另外,将应用程序池的启动模式从"OnDemand"改为"AlwaysRunning"也有类似的效果,它会在IIS启动时自动启动应用程序池,并保持它一直运行。这样,你的定时器就可以始终在后台运行,而不会被回收。

总之,如果你使用定时器来实现定时任务,那么你需要确保它一直在后台运行,否则可能会导致任务无法按照预期执行。配置IIS应用程序池的闲置时间和启动模式可以帮助你解决这个问题。