четверг, 11 октября 2012 г.

Автоматизация. Оповещаем пользователей о недоступности сервера по электронной почте с помощью JCron Joomla.

 Задача: Есть выделенный веб-сервер, предоставляющий данные по http  в режиме 24/7. В случае недоступности сервера, оповещать об этом всех пользователей по e-mail.

Подготовка:
Порассуждаем. Первое что приходит в голову: на сам сервер кидается еще один канал мобильного интернета, через который и оповещаются пользователи, если отваливается основной. А что делать, если сервер обесточен? Значит нужен второй сервер с которого и пингуется основной. Так есть же отличные он-лайн сервисы делающие это за тебя! Круто, но на серваке закрыт весь забугорный трафик из соображений безопасности. А в родной Беларуси такие сервисы не хостятся. Нет, конечно у нас тоже есть такие предложения, но все платные, начальство на это не пойдет. Значит, нам нужен свой второй сервер с которого и пингуется основной. А где его взять? Сервер-то один... а хотя... у организации есть сайт! Сайт, который физически живет в белорусском дата-центре. То что надо :) Приступим к решению.


Сайт самый простой, написан на бесплатном движке Joomla. Это очень хорошо, т.к. у нас уже есть модуль массовой е-мэил рассылки ccNewsletter. Осталось только написать скрипт, проверяющий доступность сервера. Хотя нет, не только. Скрипт же еще должен выполняться по расписанию, скажем, каждые пол часа. Значит, нам нужен некий планировщик. Гуглим. Вуаля! Модуль Jcron. Как можно понять из названия, это крон для джумлы.

Установка JCron: 
Качаем, устанавливаем... Ага, щаз. Ошибка:
 JInstaller::install: Ошибка SQL DB function failed with error number 1064You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'TYPE=MyISAM' at line 14 
Ошибка происходит при попытке создать таблицу. В чем же дело?  Смотрим версию MySQL на сервере - она 5.5.27-28.1. Гуглим. Ясно, в версии MySQL 5.5 изменился синтаксис с TYPE=MyISAM на ENGINE=MyISAM
Правим установочный скрипт в архиве с модулем. Нас интересуют файл ...\admin\install\install.sql
Вносим правки, упаковываем, пробуем установить... готово!

Проектирование:
Как же это будет работать и что же будет деать скрипт?
Прикинем. Jcron будет каждые пол-часа запускать php скрипт, который будет работать по данному алгоритму:
Тут все понятно. Переменная Доступность показывает, был ли доступен хост при прошлом запуске, а переменная Рассылка помнит, совершалась ли рассылка с момента последней смены состояния. Таким образом, оповещение о неработоспособности будет происходить если хост недоступен второй вызов подряд.  

Настройка базы данных:
Итак, для начала выясним где модуль е-мэил рассылки хранит список адресов. У меня эта таблица под названием jos_ccnewsletter_subscribers.
Также нам нужна таблица для хранения переменных Доступность и Рассылка между вызовами скрипта. Создадим её:
CREATE TABLE `pingmessenger` (
  `param` varchar(255) NOT NULL DEFAULT '',
  `val` boolean NOT NULL DEFAULT '1'
  )ENGINE = InnoDB;
INSERT INTO pingmessenger VALUES('availability','1');
INSERT INTO pingmessenger VALUES('delivery','1');
Реализация:
Ну и сам php-скрипт:
<?php
/* адрес хоста (должна отдаваться html страница) */
$url = 'http://адрес веб-станицы';
/* Настройки для подключения к БД */
$hostname = "адрес вашего мускул-сервера";
$username = "мускул-пользователь";
$password = "пароль";
$dbName = "имя БД";

/* создаём соединение */
mysql_connect($hostname,$username,$password) OR DIE("Не могу создать соединение ");
/* выбираем базу данных. Если произойдет ошибка - выводим её */
mysql_select_db($dbName) or die(mysql_error());

/*Считываем значения из БД*/
$query = "SELECT val FROM pingmessenger WHERE param='availability'";
/* Выполнить запрос. Если произойдет ошибка - вывести ее. */
$res = mysql_query($query) or die(mysql_error());
$row=mysql_fetch_array($res);
$av=$row['val'];
$query = "SELECT val FROM pingmessenger WHERE param='delivery'";
$res = mysql_query($query) or die(mysql_error());
$row=mysql_fetch_array($res);
$de=$row['val'];
/*И выводим их*/
echo '<br>Значения в БД при вызове скрипта:';
echo '<br>Доступность:'.$av;
echo '<br>Рассылка:'.$de;

/*Считываем АКТИВНЫХ пользователей из БД рассылки модуля ccNewsletter*/
$query = "SELECT * FROM jos_ccnewsletter_subscribers WHERE enabled=1";
$res = mysql_query($query) or die(mysql_error());
$number = mysql_num_rows($res);
/*Выведем их*/
$names = array(); /* массив имен пользователей*/
$emails = array(); /* массив электронных адресов*/
if ($number == 0) {
    echo "<P>Активных получателей нет!";
} else {
    echo "<P>Активных получателей: $number<BR><BR>";
    /* Получать по одной строке из таблицы в массив $row, пока строки не кончатся */
    while ($row=mysql_fetch_array($res)) {
    array_push($names, $row['name']);
    echo " ".$row['name']." хочет получать письма. ";
    array_push($emails, $row['email']);
    echo "Его Email: ".$row['email'];
    echo "<BR>";
  }
  echo "<BR>";
}

/*Пробуем открыть страницу на хосте (проверяем доступность)*/
ini_set('default_socket_timeout', '10');
$fp = fopen($url, "r");
$resurs = fread($fp, 100);
fclose($fp);
if (strlen($resurs) > 0)
{
    echo 'Сайт '.$url.' доступен';
    if ($av==0) /* был недоступен, стал доступен */
    {
        $delivers=count($names);
        $x=0;
        while ($x++<$delivers)
        {
        $subj = "Тема письма";
        $text = "Уважаемый ".$names[$x-1].", сообщаем Вам, что нихрена не работает";
        mail($emails[$x-1], $subj, $text);
        }
        $av=1;
        $de=1;
    }
}   
else
{
    echo 'Сайт '.$url.' не доступен';
    if ($av==1) /* был доступен, стал недоступен */
    {
        $av=0;
        $de=0;
    }
    else /* был недоступен, сейчас тоже недоступен */
    {
        if ($de==0)
        {
            $delivers=count($names);
            $x=0;
            while ($x++<$delivers)
            {
                $subj = "Тема письма";
                $text = "Уважаемый ".$names[$x-1].", сообщаем Вам, что все заработало!";
                mail($emails[$x-1], $subj, $text);
            }
            $av=0;
            $de=1;
        }
    }
}
/*Обновляем содержимое БД */
$query = "UPDATE pingmessenger
SET val=$av WHERE param='availability'";
$res = mysql_query($query) or die(mysql_error());
$query = "UPDATE pingmessenger
SET val=$de WHERE param='delivery'";
$res = mysql_query($query) or die(mysql_error());
/* Закрыть соединение */
mysql_close();
/* Выведем значения переменных при завершении скрипта */
echo '<br>Значения в БД после вызова скрипта:';
echo '<br>Доступность:'.$av;
echo '<br>Рассылка:'.$de;
?>
В данном случае на нашем сервере запущен IIS, который отдаёт html-страницу при соединении с ним по протоколу http, доступность которой и является идентификатором работоспособности. Если доступ к серверу надо проверять с использованием другого протокола либо порта, можно попробовать использовать fsockopen() , а не fopen(). Выглядеть это будет так:

<?php
$host="адрес хоста";
$port=номер порта;
$fp = fsockopen ($host, $port, $errno, $errstr, 30);
if (!$fp) {
echo "Сервер недоступен $errstr ($errno)<br>\n";
} else {
echo "Сервер доступен $errstr ($errno)<br>\n";
}
fclose ($fp);
}
?>

 Добавление задачи в JCron:
Все готово. Осталось только настроить расписание в JCron. Для этого зайдём в джумловскую админку и перейдем в меню настройки заданий Jcron: Компоненты->JCron->Manage Cron Jobs.
Первым делом запускаем сам крон в меню параметры
 И добавляем задание:
Вот и всё.
UPD Слегка перемудрил, можно было сразу использовать Cron, попасть в его настройки обычно можно через Cpanel. Сказался большой опыт работы c Drupal где Cron-модуль встроен в CMS "из коробки".

Комментариев нет:

Отправить комментарий