這段時間在做一個關於數據交互的項目。接收到客戶發送過來的文件后,通過Windows服務將文件按一定的規則分發到不同的MQ消息隊列,然后再由不同的處理程序處理。雖然在編碼中盡可能的考慮到了異常以及記錄了詳細的日志,但是服務還是偶爾抽風停掉了,這樣就造成了文件堆積,客戶請求得不到及時的響應。所以需要一個守護進程來保證服務始終是啟動狀態的。
首先,要保證需要監控的進程可配置,以及指定日志的保存位置。在App.config中配置日志保存路徑以及需監控進程和對應的exe可執行程序的位置(之前考慮是如果沒有該服務,則使用exe安裝服務)。
<appSettings> <add key="LogPath" value="D:\Study"/> <add key="YTGDataExchangeProcess" value="d:\Study\YTGDataExchangeProcess.exe"/> </appSettings>
public class ServiceSearch { static List<ServiceProcessInfo> _ServiceList = new List<ServiceProcessInfo>(); static object _logLocker = new Object(); //日志記錄位置 static readonly string _logPath; static ServiceSearch() { try { string[] appSettingsKeys = ConfigurationManager.AppSettings.AllKeys; foreach (string item in appSettingsKeys) { string value = ConfigurationManager.AppSettings[item].Trim(); //日志配置 if (item == "LogPath") { if (!string.IsNullOrEmpty(value)) { _logPath = string.Format("{0}\\ProcessMonitorLog", value); if (!Directory.Exists(_logPath)) { Directory.CreateDirectory(_logPath); } } } else { if (!string.IsNullOrEmpty(value)) { //應用程序是否存在 if (File.Exists(value)) { _ServiceList.Add(new ServiceProcessInfo { ServiceExePath = value, ServiceName = item }); } } } } } catch (Exception ex) { WriteLog(ex.Message); } } public static void StartMonitor() { if (_ServiceList.Count != 0) { foreach (ServiceProcessInfo info in _ServiceList) { string sevName = info.ServiceName; string sevExePath = info.ServiceExePath; if (!string.IsNullOrEmpty(sevExePath)) { //校驗exe文件是否存在 if (File.Exists(sevExePath)) { ScanServiceManager(sevName, sevExePath); } } } } else { WriteLog("沒有配置需要監控的服務!"); } } /// <summary> /// 查找服務的進程 /// 有則判斷進程是否運行,非運行狀態則啟動 /// </summary> public static void ScanServiceManager(string serviceName, string serviceExePath) { try { ServiceController sevCtrler = new ServiceController(serviceName); //該服務已存在 //如果服務狀態不為啟動 if (sevCtrler.Status != ServiceControllerStatus.Running) { sevCtrler.Start(); } //創建監控線程 WatchService(serviceName); } catch (Exception ex) { //該服務不存在 WriteLog(string.Format(ex.Message)); } } /// <summary> /// 為配置的進程添加監控進程 /// </summary> /// <param name="pro"></param> /// <param name="processAddress"></param> public static void WatchService(string ServiceName) { ServiceMonitor monitor = new ServiceMonitor(ServiceName); Thread thread = new Thread(new ThreadStart(monitor.Monitor)); thread.IsBackground = true; thread.Start(); } /// <summary> /// 寫日志 /// </summary> /// <param name="errMessage"></param> public static void WriteLog(string errMessage) { string logPath = _logPath; //沒有配置日志目錄,不記錄 if (!string.IsNullOrEmpty(logPath)) { lock (_logLocker) { string fullName = string.Format("{0}\\{1}.log", logPath, DateTime.Now.ToString("yyyy-MM-dd")); if (!File.Exists(fullName)) { File.Create(fullName).Close(); } using (StreamWriter sw = new StreamWriter(fullName, true, Encoding.UTF8)) { sw.WriteLine(String.Format("[{0}]{1}", DateTime.Now.ToString("hh:mm:ss fff"), errMessage)); sw.Close(); } } } } public class ServiceProcessInfo { /// <summary> /// 服務名稱 /// </summary> public string ServiceName { get; set; } /// <summary> /// 應用程序位置 /// </summary> public string ServiceExePath { get; set; } } }
上面ScanServiceManager方法中,使用serviceName實例化ServiceController對象,並啟用一個單獨的線程對其輪詢,監控其狀態;否則只會對第一個服務進行監控后阻塞。
public class ServiceMonitor { /// <summary> /// 服務名稱 /// </summary> private string _ServiceName; public string ServiceName { get { return _ServiceName; } set { _ServiceName = value; } } public ServiceMonitor(string ServiceName) { this._ServiceName = ServiceName; } /// <summary> /// 監聽服務 /// </summary> public void Monitor() { while (true) { try { ServiceController ctrler = new ServiceController(_ServiceName); if (ctrler.Status != ServiceControllerStatus.Running) { ServiceSearch.WriteLog(string.Format("正在啟動服務{0}...", _ServiceName)); ctrler.Start(); ServiceSearch.WriteLog(string.Format("服務{0}啟動成功!", _ServiceName)); } } catch (Exception ex) { ServiceSearch.WriteLog(string.Format("服務{0}啟動失敗,錯誤原因:{1}", _ServiceName, ex.Message)); } Thread.Sleep(1000 * 40); } } }