Pull to refresh

Автоматический перезапуск IIS службой при нарушении в ее работе

Пишем само-восстанавливающийся сервер на IIS


image

Я решил поделиться с Хаброюзерами полезной на мой взгляд программкой, многие сталкивались в работе IIS когда возникает какой то сбой и не всегда можно определить чем он вызван, но сервер перестает работать, возвращает ошибку 500 или еще что то в этом духе, обычно такое лечится перезапуском IIS, но не всегда рядом с сервером есть человек, который может это сделать, в связи с чем возникла идея, почему бы не делать это автоматически, в таком случае восстановление заняло бы максимум 5-10 минут и без присутствия администратора.

Такой подход сработает со многими продуктами, такими как Sharepoint, Webtutor (кто видел тот меня поймет) и любыми другими платформами на IIS, после переделки можно работать и с другими службами, если это нужно.
Описывать код, листинг которого не вижу особого смысла, т.к. его полностью прокомментировал, разберется любой кто хоть немного знаком с C#.
Надеюсь данное приложение поможет кому то в его работе. Буду рад услышать комментарии и пожелания.

P.S. если нужен готовый исходник пишите e-mail в комментариях.

Листинг программы под спойлером
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Globalization;
using System.Linq;
using System.Net;
using System.ServiceProcess;
using System.Text;
using System.Threading;
//using System.Threading.Tasks;

namespace WebTutorMonitor
{

    public partial class Service1 : ServiceBase
    {
// Коды, ничего не значат, просто цифры
private  int Code_Init=101;
private int Code_Start=102;
private static int Code_CreateLog=103;
private int Code_Stop=104;
private int Code_Resume=105;
private int Code_Pause=106;
private static int Code_DoneOneTask=107;
private int Code_TascControllerStarted=108;
private static int Code_TastWTMonitoringRun=109;

static bool Testing = true;//Если true сервис будет работать с тестом, не с боем
static bool DoStopStart = false;//Если false реально запуска и остановки не будет, только для проверки

static string testserverurl = @"http://webtutor.orient.root.biz";
static string prodserverurl = @"http://wtapp01";
static string testservername = @"webtutor.orient.root.biz";
static string prodservername = @"wtapp01";

        public Service1()
        {
            EventLog ev = new EventLog("Application", System.Environment.MachineName, "WebTutorMonitor");
            //Очистка журнала событий, раскоментировать если нужно почистить журнал
            //EventLog.Clear();
            InitializeComponent();
            CreateLog("Служба WebTutorMonitor инициaлизируется.",EventLogEntryType.Information,Code_Init );
            if (Testing)//Старт в режиме приложения, для Debug'a, произойдет только если Testing=True
            {
                OnStart(null);
            }
        }

        //Действия при запуске службы
        protected override void OnStart(string[] args)
        {
            //Запуск службы
            CreateLog("Служба WebTutorMonitor запущена успешно.", EventLogEntryType.Information,Code_Start);
            MainThread(); //Главный процесс службы
        }

        //Запись события в журнал событий
        static void CreateLog(string text, EventLogEntryType Type,int Code)
        {
            Thread.Sleep(1500);
            if (!(EventLog.SourceExists("WebTutorMonitor", System.Environment.MachineName)))
            {
                EventLog.CreateEventSource("WebTutorMonitor", "WebTutorLog", System.Environment.MachineName);
                CreateLog("Создан журнал событий WebTutorMonitor", EventLogEntryType.Information ,Code_CreateLog);
            }
            EventLog ev = new EventLog("Application", System.Environment.MachineName, "WebTutorMonitor");
            ev.WriteEntry(text, Type, Math.Abs (Code));
            ev.Close();
            GC.Collect();
        }

        //Действия при остановке службы
        protected override void OnStop()
        {
            CreateLog("Служба WebTutorMonitor остановлена.", EventLogEntryType.Warning ,Code_Stop);
            TimerCanceled = true;
        }

        //Действия при возобновлении работы службы
        protected override void OnContinue()
        {
            CreateLog("Служба WebTutorMonitor запущена после паузы.",EventLogEntryType.Information ,Code_Resume); 
            base.OnContinue();
        }

        //Действия при паузе службы
        protected override void OnPause()
        {
            CreateLog("Служба WebTutorMonitor приостановлена.", EventLogEntryType.Warning,Code_Pause ); 
            base.OnPause();
        }

        //Главный поток службы
        void MainThread()
        {
            //Запуск обработки заданий в отдельном потоке, чтобы освободить текущий поток для диспетчера служб
            System.Threading.Thread tr = new System.Threading.Thread(new System.Threading.ThreadStart (TimerTask));//Работа потока проходит в процедуре TimerTask
            tr.Start();//Запуск процедуры TimerTask в отдельном потоке.
            CreateLog("Менеджер заданий запущен.", EventLogEntryType.Information , Code_TascControllerStarted);
        }

       static bool TimerCanceled = false;//Если True поток сервиса получает комманду на остановку

        //Основная процедура службы
        static private void TimerTask()
        {
            CreateLog("Задача монитор WT запущена.", EventLogEntryType.Information, Code_TastWTMonitoringRun); 
            rr:
                rcl++;
                if (TimerCanceled)//Если получен True выкодим из потока
                {
                    CreateLog("Логирование завершено  " + DateTime.Now.ToString() + ". Выполнено " + rcl.ToString() + " итераций логирования.", EventLogEntryType.Information, Code_DoneOneTask);
                    goto ext;
                }
                else
                {
                    StartLogIteration();//Если еще рано выходить запускаем итерацию рабочего процесса
                }
                Thread.Sleep(100);
                goto rr;
            ext:
                return; //Выход из потока
        }

        //Выключить себя, можно применять для завершения работы службы
        static void ShutdownMeNow(string reason)
        {
            CreateLog("Завершение работы службы: " + reason, EventLogEntryType.Information, Code_DoneOneTask);
            ServiceController sc = new ServiceController("Webtutormonitor", System.Environment.MachineName);//Webtutormonitor это название службы
            if (sc.CanStop == true)
            {
                sc.Stop();//Посылаем команду на отключение службы на текущем компьютере
            }
        }

        //Выполняем действие (сканируем или что либо другое)
       static  void StartLogIteration()
        {
            RunNewScan(); //Сканируем сервер на работоспособность
            //CreateLog("Работа выполнена  " + DateTime.Now.ToString() , EventLogEntryType.Information, Code_DoneOneTask); //Можем написать что бы сделали действие в лог
            System.Threading.Thread.Sleep(5000);//Поспим, чтобы не слишком часто работать
        }

        //Выполняем сканирование сервера
      static void RunNewScan()
       {
           string statdisk;//Переменная для хранения коментария ответа от сервера
           string urlparam;//Переменная для хранения адреса сервера
           if (Testing)//Если тестируем то тестовый сервер, если нет то боевой
           {
               urlparam = testserverurl;//тестовый
           }
           else { urlparam = prodserverurl; }//боевой
           int stat = ServerStat(out statdisk, new Uri(urlparam));//Получаем данные от сервера, его текущее состояние
           if (stat!=200)//Если не 200 значит у нас проблемы
           {
               CreateLog("Ошибка в работе WT: " + statdisk, EventLogEntryType.Error  , stat);
               if (stat==503)//Если 503 значит он в процессе перезапуска либо у него проблема с учетной записью
               {
                   DoRestartIIS();//Если первое проверим, перезапустим если нужно
               }
               else//другие ошибки
               {
                   if (stat==404)//Сервер выключен, пробуем включить
	                {
                        if (Testing)//Если тестируем то тестовый сервер, если нет то боевой
                          {
                              StartIISWTNow(testservername);//тестовый
                          }
                        else { StartIISWTNow(prodservername); }//боевой
	                }
               }
           }
       }

        //процедура перезапуска IIS
      static void DoRestartIIS()
      {
          ServiceController sc;//Контроллер сервисов
          //Инициируем контроллер для нужного нам сервиса
          if (Testing)//Если тестируем то тестовый сервер, если нет то боевой
           {
               sc = new ServiceController("W3SVC", testservername);//тестовый
           }
           else
           {
               sc = new ServiceController("W3SVC", prodservername);//боевой
           }
          CreateLog("Предпринимаю попытку восстановить сервер путем перезапуска IIS!", EventLogEntryType.Error, 1001);

          //Пробуем выключить службу веб-публикаций на сервере
          if (Testing)//Если тестируем то тестовый сервер, если нет то боевой
           {
                CreateLog("Типа выключаю сервис!", EventLogEntryType.Error, 1202);
                ShutdownIISWTNow(testservername);//тестовый
           }
          else { ShutdownIISWTNow(prodservername); }//боевой
          //Если мы тут, то мы смогли выключить службу, иначе до сих пор долбимся в попытках ее отключить
          //Служба отключена- включаем ее обратно
          if (Testing)//Если тестируем то тестовый сервер, если нет то боевой
          {
               CreateLog("Типа включаю сервис!", EventLogEntryType.Error, 1201);
               StartIISWTNow(testservername);//тестовый
          }
          else { StartIISWTNow(prodservername); }//боевой
          //Если мы тут, значит сервер сказал что служба запущена и работает
          Thread.Sleep(5000);//Дадим немного времени
          sc.Refresh();//Проверим, может врет?
          //Выводим текущее состояние в лог
          CreateLog("Сервер в состоянии  " + sc.Status.ToString(), EventLogEntryType.Error, 1301);
          if (sc.Status == ServiceControllerStatus.Running )//Если сработало пишем соответствующую запись в лог, если нет тоже
          {
              CreateLog("Попытка перезапутить IIS Удалася!:) ", EventLogEntryType.Error, 1101);
          }
          else
          {
              CreateLog("Не могу сделать рестарт IIS Функция отключена:( ", EventLogEntryType.Error, 1101);
          }
      }

      //Процедура выключения IIS на сервере, которая используется в процедуре DoRestartIIS
      static void ShutdownIISWTNow(string compname = testservername)
      {
          ServiceController sc = new ServiceController("W3SVC", compname);//Инициализируем контроллер для данного сервера, W3SVC это имя службы веб публикаций на сервере Windows Server 2008 R2
      gg: sc.Refresh();//Тут точка возврата, будем возвращаться сюда, если не получилось выключить службу
          CreateLog("Попытка завершения службы на сервере " + compname, EventLogEntryType.Information, Code_DoneOneTask);
          if (sc.CanStop == true)//Спрашиваем у сервере, можем ли мы отключить сейчас iis
          {
              try //Если да, то пробуем это сделать
	            {
                    if (DoStopStart)//Если false не будем реально ничего делать, просто сделаем вид что пытались
                    {
                        sc.Stop();//Посылаем комманду на отключение службы
                        CreateLog("Сервер получил команду на остановку службы :)", EventLogEntryType.Error, 1004);
                    }
                    else
                    {
                        CreateLog("Сервер не получил команду на остановку службы потому что приложение работает в демо режиме", EventLogEntryType.Error, 1204);
                    }
	            }
	            catch (Exception eee)//Если возникла ошибка пишем ее в лог
	            {
		            CreateLog("Не получилось остановить службу :( " + eee.Message , EventLogEntryType.Error , 1005);
	            }

          }
          else//Если сервер говорит, что нельзя останавливаться идем сушить бублики до следующего раза
          {
              CreateLog("Её нельзя сейчас останавливать :( Статус сервера: " + sc.Status.ToString(), EventLogEntryType.Error, 1003);
          }
          if (sc.Status == ServiceControllerStatus.Stopped)//Если сервер говорит что служба остановлена радуемся, у нас получилось
          {
              CreateLog(@"Я остановил службу!!! =8 Ха! Ха! хА! =\" + sc.Status.ToString(), EventLogEntryType.Warning, 1043);
              return;//Выходим, чтобы попробовать запустить ее снова.
          }
          Thread.Sleep(10000);//если мы пришли сюда значит сервер еще не остановился, а значит у нас почему то не получилось его остановить, 
          //может быть он просто сейчас пытается остановиться но  еще не успел закончить, подождем 10 секунд и попробуем снова
          //так как не остановив запускать его бессмысленно будем долбиться пока не сможем его выключить
          goto gg;//пробуем снова
      }

      //Запуск IIS все  по аналогии с остановкой.
      static void StartIISWTNow(string compname = testservername)
      {
          ServiceController sc = new ServiceController("W3SVC", compname);
      gg: sc.Refresh();
          CreateLog("Запуска службы на сервере " + compname, EventLogEntryType.Information, Code_DoneOneTask);
          if (sc.Status == ServiceControllerStatus.Stopped)
          {
              try
              {
                  if (DoStopStart)
                  {
                      sc.Start();
                      sc.Refresh();
                      sc.WaitForStatus(ServiceControllerStatus.Running , new TimeSpan(0, 0, 10));
                      CreateLog("Сервер получил команду на запуск службы :)", EventLogEntryType.Warning, 1211);
                  }
                  else
                  {
                      CreateLog("Сервер не получил команду на запуск службы потому что приложение работает в демо режиме", EventLogEntryType.Error, 1204);
                  }  
              }
              catch (Exception eee)
              {
                  CreateLog("Не получилось запустить службу :( " + eee.Message, EventLogEntryType.Error, 1012);
              }

          } else
              {
                  CreateLog("Её нельзя сейчас запускать :( Статус сервера: " + sc.Status.ToString(), EventLogEntryType.Error, 1013);
              }
            if (sc.Status == ServiceControllerStatus.Running )
              {
                  CreateLog("У меня получилось запустить службу:)" , EventLogEntryType.Warning , 1023);
                  return;
              }
          Thread.Sleep(10000);
          goto gg;
      }

      //Определяем статус сервера в данный момент, descript это дополнительная информация о состоянии, текст ошибки например, url-это адрес узла сервера для проверки
      static int ServerStat(out string descript, Uri url)
       {
           int statusCode=-1;
           var myCred;//учетные данные, т.к. мы будем входить на сервер IIS под NTLM, передалать на другие типы авторизации проще
           if (Testing)//Если тестируем то тестовый сервер, если нет то боевой
           {
               myCred = new NetworkCredential("vipuhov", "123456", "ORIENT");//тестовый тут мы используем такие учетные данные
           }
           else { myCred = CredentialCache.DefaultNetworkCredentials; }//боевой тут мы настроили под какими учетными данными будет запускаться служба
           Uri urll = url;
           var myCache = new CredentialCache { { urll, "NTLM", myCred } };//тут мы привязываем учетные данные к адресу сайта, чтобы установить NTLM авторизацию
           try
           {
               var request = (HttpWebRequest)WebRequest.Create(urll);//создаем объект веб запроса
               request.Timeout = 50000;//устанавливаем таймаут соединения, 50 секунд это перебор для любого сайта, можно говорить что ему плохо если он минуту страницу показать не может
               //настраиваем учетные данные и метод, нам сама страница не нужна, поэтому берем только заголовок
               request.Credentials = myCache;
               request.Method = WebRequestMethods.Http.Head;
               request.Accept = @"*/*";
               var response = (HttpWebResponse)request.GetResponse();//делаем запрос
               statusCode = (int)response.StatusCode;//получаем код ответа от сервера
               response.Close();//закрываем запрос, нам он больше не нужен
               GC.Collect();//убираем мусор:)
               descript = "OK";//если мы тут значит все ОК:)
               return statusCode;//возвращаем код, скорее всего 200
           }
           catch (WebException ex)//тут ловим ошибки
           {
               if (ex.Response == null)//если нет ответа от сервера скорее всего ошибка 404 сервер недоступен
               {
                   descript = ex.Message;//возвращаем текст ошибки
               }
               else
               {
                   statusCode = (int)((HttpWebResponse)ex.Response).StatusCode;//если запрос прошел но сервер возвратил ошибку пишем ошибку возвращаем его код
                   descript = ((HttpWebResponse)ex.Response).StatusDescription;//пишем сообщение которое нам прислал сервер вместе с ошибкой
               }
               GC.Collect();//чистим мусор:)
               return statusCode;//возращаем код ошибки
           }
       }

    }
}

Tags:
Hubs:
You can’t comment this publication because its author is not yet a full member of the community. You will be able to contact the author only after he or she has been invited by someone in the community. Until then, author’s username will be hidden by an alias.