最近要写个从页面上点击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帐号也不顶用,抓狂中。。。
先研究到这边吧,下周有新成果后再更新