"當ScriptTimeout發生時,ASPX會繼續執行? 還是嘎然而止?"

以上問題的答案將牽動IIS的調校哲學,當系統發生問題時而導致大量Request處理逾時,調整ScriptTimeout的長短,會產生何種效應?

在過去的印象中,如果使用者開啟一個要執行很久的ASP/ASPX程式,在程式未執行完成之前,使用者關閉瀏覽器時,ASPX仍會繼續跑完,不受與使用者間連線中斷的影響。我用以下的Code進行驗證...

   1:  private void writeLog(string msg)
   2:  {
   3:      using (System.IO.StreamWriter sw =
   4:          new System.IO.StreamWriter("d:\\temp\\longrun.log", true))
   5:      {
   6:          sw.WriteLine(
   7:              string.Format("{0:yyyy-MM-dd HH:mm:ss.fff} {1}",
   8:              DateTime.Now, msg
   9:          ));
  10:      }
  11:   
  12:  }
  13:  protected void Page_Load(object sender, EventArgs e)
  14:  {
  15:      //Write start log
  16:      writeLog("Start!");
  17:      //Wait for 20 seconds
  18:      System.Threading.Thread.Sleep(20000);
  19:      //Write end log
  20:      writeLog("Finished!");
  21:  }

測試時,開啟IE連上該網頁後立即將IE關閉。20秒後檢查longrun.log,可以看到相隔20秒的兩列Log。於是我們得到證明: 使用者關閉Browser並不妨礙ASPX繼續執行

2007-06-21 04:20:51.814 Start!
2007-06-21 04:21:11.832 Finished!

那麼,當ScriptTimeout發生時又是如何? 我將web.config <httpRuntime> executionTimeout設為5秒,跑了以下的程式:

   1:  protected void Page_Load(object sender, EventArgs e)
   2:  {
   3:      //Write start log
   4:      writeLog("Start!");
   5:      for (int i = 0; i < 30; i++)
   6:      {
   7:          System.Threading.Thread.Sleep(1000);
   8:          //One log per second
   9:          writeLog("Loop-" + i.ToString());
  10:      }
  11:      //Write end log
  12:      writeLog("Finished!");
  13:      //Response.Write something    
  14:      Response.Write("Done!");
  15:      Response.End();
  16:  }

大約5-15秒左右(依據這串討論中MS Online Support的說法,ScriptTimeout小於1分鐘,會有5-15秒的延遲),網頁傳回以下的錯誤:
[HttpException (0x80004005): Request timed out.]

在Log則看到有趣的事,30圈的Loop沒有跑完,在i==5時停住了。

2007-06-21 04:26:50.561 Start!
2007-06-21 04:26:51.561 Loop-0
2007-06-21 04:26:52.561 Loop-1
2007-06-21 04:26:53.561 Loop-2
2007-06-21 04:26:54.561 Loop-3
2007-06-21 04:26:55.561 Loop-4
2007-06-21 04:26:56.561 Loop-5

不過我們還不滿足,希望得到更明確的ScriptTimeout證據。但,程式都中止了,還能再做些什麼事? 之前談過Response.End時產生的ThreadAbortException可以被catch抓到,而我猜ScriptTimeout所引發的中止應該也可以被catch捕捉下來。於是改寫Code如下:

   1:  protected void Page_Load(object sender, EventArgs e)
   2:  {
   3:      //Write start log
   4:      writeLog("Start!");    
   5:      try
   6:      {
   7:          for (int i = 0; i < 30; i++)
   8:          {
   9:              System.Threading.Thread.Sleep(1000);
  10:              writeLog("Loop-" + i.ToString());
  11:          }
  12:      }
  13:      catch (Exception ex)
  14:      {
  15:          writeLog("ERROR: " + ex.Message);
  16:      }
  17:      //Write end log
  18:      writeLog("Finished!");
  19:      //Response.Write something    
  20:      Response.Write("Done!");
  21:      Response.End();
  22:  }

Log結果如下:

2007-06-21 04:40:37.580 Start!
2007-06-21 04:40:38.580 Loop-0
2007-06-21 04:40:39.580 Loop-1
2007-06-21 04:40:40.580 Loop-2
2007-06-21 04:40:41.580 Loop-3
2007-06-21 04:40:42.580 Loop-4
2007-06-21 04:40:43.580 Loop-5
2007-06-21 04:40:44.580 Loop-6
2007-06-21 04:40:45.580 Loop-7
2007-06-21 04:40:46.580 Loop-8
2007-06-21 04:40:47.580 Loop-9
2007-06-21 04:40:48.580 Loop-10
2007-06-21 04:40:49.580 Loop-11
2007-06-21 04:40:50.002 ERROR: Thread was being aborted.

抓到了!! cath (Exception ex)抓到了ThreadAbortException。由這項證據可以得知,當程式執行時間超過ScriptTimeout時,W3WP.exe應該是用類似Response.End()的方式強制結束ASPX網頁的執行。

由以上的觀察,我的個人推論是--當程式異常時,設定較短的ScriptTimeout應有助於改善大量Thread被Hang住的狀況,盡早釋放出Resource處理其他仍然健全的功能。但如果程式是卡在Unmanaged World Resource,Thread.Abort並不能讓程式立即由困境中脫身(只能先在Thread上做記號,待控制權交回Managed World時才生效)。另外,以下兩則延伸閱讀中提到Multithread下Thread.Abort可能產生的負面效應,可做為進階設計時的考量。

延伸閱讀:
1.How To Stop a Thread in .NET (and Why Thread.Abort is Evil)
2.Plumbing the Depths of the ThreadAbortException Using Rotor


Comments

Be the first to post a comment

Post a comment