最近要写个从页面上点击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帐号也不顶用,抓狂中。。。


先研究到这边吧,下周有新成果后再更新