最近要寫個從頁面上點擊IISRESET按鈕,就把服務器IISREST的功能。
難點:
1. IISREST後程序池停掉,失去EnvAgent的連接,response會失去,就不能返回狀態,來判斷是否IISRESET成功
2. IISREST由於成功率不高,必須判斷是否成功啓動,要是沒有成功啓動,影響很大,所以想到如下方法判斷:
IISRESET >D:\\tmplog.txt
該命令會把執行結果放到D:\\tmplog.txt中,如果內容如下就可以判斷是啓動成功了
正在嘗試停止... Internet 服務已成功停止 正在嘗試啓動... Internet 服務已成功啓動
所以,思路如下:
先發送請求,執行 “IISRESET >D:\\tmplog.txt”這條命令
在等待一定時間,如40秒(等程序池啓動起來),發送檢查結果命令,
該命令判斷tmplog.txt文件是否是最新的(最後修改時間是否是一分鐘內),
再判斷內容是否正確,
如果都正確,則IISRESET成功
(該代碼已被淘汰,複製請看最後的代碼)
核心代碼如下
#region IISRest /// <summary> /// IISRESRT /// </summary> public static void IISReset() { try { Logger.Info("IIS Reset Start"); string filePath = "d:\\templog.txt"; if (File.Exists(filePath)) { File.Delete(filePath); } System.Diagnostics.Process p = new System.Diagnostics.Process(); p.StartInfo.FileName = "cmd.exe"; p.StartInfo.UseShellExecute = false; //是否使用操作系統shell啓動 p.StartInfo.RedirectStandardInput = true;//接受來自調用程序的輸入信息 p.StartInfo.RedirectStandardOutput = true;//由調用程序獲取輸出信息 p.StartInfo.RedirectStandardError = true;//重定向標準錯誤輸出 p.StartInfo.CreateNoWindow = true;//不顯示程序窗口 p.Start();//啓動程序 //向cmd窗口發送輸入信息 p.StandardInput.WriteLine("IISRESET > " + filePath + "&exit"); p.StandardInput.AutoFlush = true; //p.StandardInput.WriteLine("exit"); //向標準輸入寫入要執行的命令。這裏使用&是批處理命令的符號,表示前面一個命令不管是否執行成功都執行後面(exit)命令,如果不執行exit命令,後面調用ReadToEnd()方法會假死 //同類的符號還有&&和||前者表示必須前一個命令執行成功纔會執行後面的命令,後者表示必須前一個命令執行失敗纔會執行後面的命令 //獲取cmd窗口的輸出信息 //string output = p.StandardOutput.ReadToEnd(); //StreamReader reader = p.StandardOutput; //string line=reader.ReadLine(); //while (!reader.EndOfStream) //{ // str += line + " "; // line = reader.ReadLine(); //} p.WaitForExit();//等待程序執行完退出進程 p.Close(); } catch (Exception ex) { Logger.Error("IIS Reset Exception:" + ex.ToString()); } Logger.Info("IIS Reset Finish"); //Console.WriteLine(output); } /// <summary> /// IISRESRT /// </summary> public static void IISReset(string ip) { try { Logger.Info("IIS Reset Start"); string filePath = "d:\\templog.txt"; if (File.Exists(filePath)) { File.Delete(filePath); } System.Diagnostics.Process p = new System.Diagnostics.Process(); p.StartInfo.FileName = "cmd.exe"; p.StartInfo.UseShellExecute = false; //是否使用操作系統shell啓動 p.StartInfo.RedirectStandardInput = true;//接受來自調用程序的輸入信息 p.StartInfo.RedirectStandardOutput = true;//由調用程序獲取輸出信息 p.StartInfo.RedirectStandardError = true;//重定向標準錯誤輸出 p.StartInfo.CreateNoWindow = true;//不顯示程序窗口 p.Start();//啓動程序 //向cmd窗口發送輸入信息 p.StandardInput.WriteLine("iisreset \\"+ip+" /restart >" + filePath + "&exit"); p.StandardInput.AutoFlush = true; //p.StandardInput.WriteLine("exit"); //向標準輸入寫入要執行的命令。這裏使用&是批處理命令的符號,表示前面一個命令不管是否執行成功都執行後面(exit)命令,如果不執行exit命令,後面調用ReadToEnd()方法會假死 //同類的符號還有&&和||前者表示必須前一個命令執行成功纔會執行後面的命令,後者表示必須前一個命令執行失敗纔會執行後面的命令 //獲取cmd窗口的輸出信息 //string output = p.StandardOutput.ReadToEnd(); //StreamReader reader = p.StandardOutput; //string line=reader.ReadLine(); //while (!reader.EndOfStream) //{ // str += line + " "; // line = reader.ReadLine(); //} p.WaitForExit();//等待程序執行完退出進程 p.Close(); } catch (Exception ex) { Logger.Error("IIS Reset Exception:" + ex.ToString()); } Logger.Info("IIS Reset Finish"); //Console.WriteLine(output); } /// <summary> /// 判斷IISReset是否成功 (已在IISControl中實現,這邊代碼不完全) /// /// 由於IISReset後會失去返回,所以需要重新發送請求來獲取IISReset的結果 /// 這裏採用很短時間段裏面檢查IISREST時記錄在d:\\templog.txt中的日記來判斷 /// /// 需要判斷文件的時間某段時間內,並且內容是重啓正確 /// /// </summary> /// <returns></returns> public static bool IsResetSuccess() { string filePath = "d:\\templog.txt"; return File.ReadAllText(filePath).Trim().Equals("正在嘗試停止...\r\r\nInternet 服務已成功停止\r\r\n正在嘗試啓動...\r\r\nInternet 服務已成功啓動"); } /// <summary> /// 獲取 IISRESET 日記內容 /// </summary> /// <returns></returns> public static string GetIISResetLog() { string filePath = "d:\\templog.txt"; return File.ReadAllText(filePath, Encoding.Default).Trim(); } /// <summary> /// 獲取 IISREST 日記文件的時間差,來判斷是否是最近這次的日記 /// </summary> /// <returns></returns> public static string IISResetTimeSpan() { DateTime t1 = DateTime.Now; string filePath = "d:\\templog.txt"; FileInfo fi = new FileInfo(filePath); DateTime t2 = fi.LastWriteTime; return ExecDateDiff(t2, t1); } #endregion IISRest
Service代碼如下
public JsonResult IISReset(MachinePack pack) { var resp = new APIResponse<string>() { StateCode = StateCode.Success, Message = "" }; var accessToken = LoginSecurity.DecodeAccessToken(pack.Token); if (accessToken == null) { resp.StateCode = StateCode.Fail; resp.Message = "操作用戶無法識別!"; } else if (accessToken.ExpiredTime < DateTime.Now) { resp.StateCode = StateCode.Fail; resp.Message = "登陸信息過期!"; } else { try { var token = LoginSecurity.DecodeAccessToken(pack.Token); string ip = pack.IPAddress; Logger.Info("IISReset start by " + token.Alias+" on "+ip); IISUtil.IISReset(ip); } catch (Exception ex) { resp.StateCode = StateCode.Fail; resp.Message += ex.Message; resp.Message += ex.ToString(); } } return Json(resp, JsonRequestBehavior.DenyGet); } public JsonResult CheckIISResetStatus(MachinePack pack) { var resp = new APIResponse<string>() { StateCode = StateCode.Success, Message = "" }; var accessToken = LoginSecurity.DecodeAccessToken(pack.Token); if (accessToken == null) { resp.StateCode = StateCode.Fail; resp.Message = "操作用戶無法識別!"; } else if (accessToken.ExpiredTime < DateTime.Now) { resp.StateCode = StateCode.Fail; resp.Message = "登陸信息過期!"; } else { try { var token = LoginSecurity.DecodeAccessToken(pack.Token); EnvSub envSub = EnvSubBiz.FindByName(pack.DomainName); resp.Message += "EnvSubBiz.FindByName success;"; EnvMachine envmachine = EnvMachineBiz.FindByIP(pack.IPAddress); resp.Message += "EnvMachineBiz.FindByIP success;"; Logger.Info("IISReset status check by " + token.Alias); string timespan = IISUtil.IISResetTimeSpan(); Logger.Info("IISReset status check : timespan" + timespan); if (Double.Parse(timespan) > 40000) { resp.Data = "2"; resp.Message = "時間差:" + timespan+";"; Logger.Info("IISReset status check : timespan結果 :不是最新的"); string str = string.Format("{0} Reset IIS on {1} on {2}", token.Alias, pack.IPAddress)+" : fail"; EnvOperationLogBiz.LogOptIISReset(envSub.EnvSubId, token.Alias, str, envmachine.Id); } else { string log = IISUtil.GetIISResetLog(); resp.Message = "log:" + log + ";"; Logger.Info("IISReset status check : log :"+log); if (log.Equals("正在嘗試停止...\r\r\nInternet 服務已成功停止\r\r\n正在嘗試啓動...\r\r\nInternet 服務已成功啓動")) { resp.Data = "0"; string str = string.Format("{0} Reset IIS on {1} on {2}", token.Alias, pack.IPAddress) + " : success"; EnvOperationLogBiz.LogOptIISReset(envSub.EnvSubId, token.Alias, str, envmachine.Id); Logger.Info("IISReset status check : result : success"); } else { resp.Data = "1"; string str = string.Format("{0} Reset IIS on {1} on {2}", token.Alias, pack.IPAddress) + " : fail"; EnvOperationLogBiz.LogOptIISReset(envSub.EnvSubId, token.Alias, str, envmachine.Id); Logger.Info("IISReset status check : result : fail"); } } } catch (Exception ex) { resp.StateCode = StateCode.Fail; resp.Message += ex.Message; resp.Message += ex.ToString(); } }
3. 但是又有個問題,就是IISRESET失敗後,再發檢查結果命令時,由於程序池沒有成功啓動起來,所以返回404,
這時也接受不了IISRESET的請求,後果很嚴重。
所以想到用另一臺機器遠程IISRESET,遠程重啓命令如下:
iisreset \\10.2.8.80 /restart >D:\\tmplog.txt
如果是遠程IISRESET就不需要等40秒,用另一條語句去請求檢查結果
代碼如下:
/// <summary> /// IISRESRT /// </summary> public static bool IISReset(string ip) { Logger.Info("IISReset start"); System.Diagnostics.Process p = new System.Diagnostics.Process(); try { p.StartInfo.FileName = "cmd.exe"; p.StartInfo.UseShellExecute = false; //是否使用操作系統shell啓動 p.StartInfo.RedirectStandardInput = true;//接受來自調用程序的輸入信息 p.StartInfo.RedirectStandardOutput = true;//由調用程序獲取輸出信息 p.StartInfo.RedirectStandardError = true;//重定向標準錯誤輸出 p.StartInfo.CreateNoWindow = false;//不顯示程序窗口 p.Start();//啓動程序 //向cmd窗口發送輸入信息 p.StandardInput.WriteLine("iisreset \\\\" + ip + " /restart"); Logger.Info("IISReset send command"); p.StandardInput.AutoFlush = true; p.StandardInput.WriteLine("exit"); //向標準輸入寫入要執行的命令。這裏使用&是批處理命令的符號,表示前面一個命令不管是否執行成功都執行後面(exit)命令,如果不執行exit命令,後面調用ReadToEnd()方法會假死 //同類的符號還有&&和||前者表示必須前一個命令執行成功纔會執行後面的命令,後者表示必須前一個命令執行失敗纔會執行後面的命令 Logger.Info("IISReset read output"); //獲取cmd窗口的輸出信息 string output = p.StandardOutput.ReadToEnd(); p.WaitForExit();//等待程序執行完退出進程 p.Close(); Logger.Info("output:" + output); if (output.Contains("正在嘗試停止...\r\r\nInternet 服務已成功停止\r\r\n正在嘗試啓動...\r\r\nInternet 服務已成功啓動")) { Logger.Info("return true"); return true; } else { Logger.Info("return false"); } //StreamReader reader = p.StandardOutput; //string line=reader.ReadLine(); //while (!reader.EndOfStream) //{ // str += line + " "; // line = reader.ReadLine(); //} } catch (Exception ex) { Logger.Error("IIS Reset Exception:" + ex.ToString()); } return false; //Console.WriteLine(output); }
service代碼
public JsonResult IISReset(MachinePack pack) { var resp = new APIResponse<string>() { StateCode = StateCode.Success, Message = "" }; var accessToken = LoginSecurity.DecodeAccessToken(pack.Token); if (accessToken == null) { resp.StateCode = StateCode.Fail; resp.Message = "操作用戶無法識別!"; } else if (accessToken.ExpiredTime < DateTime.Now) { resp.StateCode = StateCode.Fail; resp.Message = "登陸信息過期!"; } else { try { var token = LoginSecurity.DecodeAccessToken(pack.Token); EnvSub envSub = EnvSubBiz.FindByName(pack.DomainName); EnvMachine envmachine = EnvMachineBiz.FindByIP(pack.IPAddress); string ip = pack.IPAddress; Logger.Info("IISReset start by " + token.Alias + " on " + ip); if (IISUtil.IISReset(ip)) { resp.Data = "0"; string str = string.Format("{0} Reset IIS on {1} on {2}", token.Alias, pack.IPAddress) + " : success"; EnvOperationLogBiz.LogOptIISReset(envSub.EnvSubId, token.Alias, str, envmachine.Id); Logger.Info("IISReset status result : success"); } else { resp.Data = "1"; string str = string.Format("{0} Reset IIS on {1} on {2}", token.Alias, pack.IPAddress) + " : fail"; EnvOperationLogBiz.LogOptIISReset(envSub.EnvSubId, token.Alias, str, envmachine.Id); Logger.Info("IISReset status result : fail"); } } catch (Exception ex) { resp.StateCode = StateCode.Fail; resp.Message += ex.Message; resp.Message += ex.ToString(); } } return Json(resp, JsonRequestBehavior.DenyGet); }
注意:
本機跟部署的服務器的機子向目標服務器方式該命令返回 RPC服務不可用,
網上查到些解決方法,試了後發現都沒用。
原因是由於上海機子的防火牆問題,不能像南通的機子發送遠程IISRSET命令,
所以找了臺南通機子布上EnvAgent
結果運行的時候,報如下錯誤:
c:\windows\system32\inetsrv>iisreset \\10.2.9.80 /restart 訪問被拒絕,必須是該遠程計算機的管理員才能使用此命令。 請將您的用戶名添加到該遠程計算機的管理員本地組或者 域管理員全局組中。
納尼,
領導說是由於域帳號,跟組帳號間帳號不通用,模擬Admin帳號也不頂用,抓狂中。。。
先研究到這邊吧,下週有新成果後再更新