1. 程式人生 > >FileSystemWatcher 導致Mono ASP.NET應用程式CPU使用率比較高

FileSystemWatcher 導致Mono ASP.NET應用程式CPU使用率比較高

大家都知道ASP.NET 網站應用程式(WebSite)可以自動檢測到你的ASP.NET應用的檔案修改,其中要使用到的就是監視磁碟上的檔案/目錄的更改,以便應用程式可以採取它認為必要檔案建立/刪除/修改事件的反應中的任何步驟的FileSystemWatcher 類

Mono的 FileSystemWatcher實現盡了最大的努力適應各種環境(Linux/Windows/*BSD),在各種作業系統環境下執行其分配的任務,在Unix環境下支援以下後端的系統:

  • FAM
  • kevent (BSD*/MacOSX only)
  • gamin
  • inotify (Linux only)
  • Managed watcher

其中,假設您執行 Linux(核心2.6.13以上), inotify是一種首選的後端機制因為它需要對使用者態應用程式的一部分,他不是使用輪詢而是使用 Linux 核心的通知機制 (在我們的例子,Mono的執行庫)。然而,它需要 Linux 核心來支援機制。

如果你的核心不支援inotify,Mono將嘗試使用FAM和gamin 這樣的使用者態的應用程式來監測檔案系統的檔案/目錄的更改,然後通知到Mono執行時,這樣效率就大打折扣了,效能就很糟糕了。如果Mono 都無法檢測到inotify,Fam以及gamin,mono將使用最後一個選項Managed watcher,此監測程式在託管程式碼中實現,併為監測、 輪詢更改所選檔案/目錄上的檔案系統使用一個單獨的執行緒。由於應用程式可能 (和在 ASP.NET 的情況下有時不會)遞迴檢視目錄,它可能會非常昂貴的情況,需要檢查更改為一大組的檔案。每個執行的變化檢測需要檢查檔案/目錄是否存在 (以防託管觀察程式這些都是兩個 stat (2) 呼叫),然後檢查更改的檔案元資料 (大小、 修改時間等),生成一個事件。大約每750ms發生一次

,並給伺服器的 CPU 上帶來大量的負載,導致CPU飆升。

解決方法也很簡單,如果你可以的話的關閉檔案系統監測 (這意味著您的ASP.NET應用程式將不自動重新啟動修改 Web.config 時,不會重新編譯檔案,如果您修改程式碼隱藏.cs 或.aspx、.ascx 檔案等)。Mono支援MONO_MANAGED_WATCHER環境變數 設定為值disable,減輕您的應用程式做上面所述的檔案系統輪詢事務,既然是生產環境,就不會有什麼經常性更新關閉這個特性也不會有大的影響,還可以節約後臺的執行緒資源 。

在linux上跑ASP.NET網站,有時cpu會出現佔用率比較高的情況,過段時間它又正常了,在VPS中,這樣情況出現的機率更大,處理方法:
1、不必管它,它自然會降下來,只是等的時間要長一點,而且有可能過段時間又出現,原因就是上述說明,如果是VPS或者雲主機上出現這個問題,你一定得好好的分析下原因,是不是就是有這個特性引起的,如果是就把他關掉;
2、使用Jexus 跑ASP.NET網站,在jws.start/jws.restart兩個檔案中,插一句:export MONO_MANAGED_WATCHER=disable,禁止ASP.NET自動檢測,當然副作用是:你修改原始碼後,得手工重啟這個網站。

3、使用Apache的 Mod_mono 使用命令 MonoSetEnv [server_alias] MONO_MANAGED_WATCHER=disable

可以使用下面的程式碼檢測你的Linux伺服器上使用的是哪個FileSystemWatcher 實現

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Reflection;

namespace FileWatchDetect
{
    class Program
    {
        static void Main(string[] args)
        {
            object watcher = new FileSystemWatcher().GetType()
                .GetField("watcher", BindingFlags.NonPublic | BindingFlags.Static).GetValue(null);
           
            Console.WriteLine(watcher != null ? watcher.GetType().FullName : "unknown");
            Console.Read();
        }
    }
}

下面的結果是在Windows Azure上的一臺OpenSuse 12.0.4上的執行:

image