вторник, 10 июля 2012 г.

PHP скрипт как Windows сервис

"Win32service - это специфическое расширение для Windows, которое позволяет PHP взаимодействовать с Менеджером Контроля Сервисов (Service Control Manager) для запуска, остановки, регистрации и удаления сервисов, и даже позволяет вашему PHP скрипту работать как сервису." документация PHP.

Установка расширения Win32service

Для установки этого расширения предлагается его скомпилировать из исходников. Но в комментариях документации к разделу об установке можно найти ссылки на готовые .dll:


Готовое расширение от stealth35 у меня не заработало. DLL по второй ссылке, хоть и устаревшая (2009 года), но работает и для моих нужд вполне подошла.

Скачиваем нужную библиотеку, в моём случае это "php_win32service-svn-20091011-5.3-vc6-x86.zip",  и подключаем её к PHP.

Построение скрипта

Прежде всего нужно придумать имя сервиса (для команд, без пробелов) и название для отображения в списке сервисов.
$service_name = 'win_service';
$display_name = 'Test windows service';
Создаваемый скрипт будет выполняться через комнадную строку, и будет получать один аргумент для управления.

Код управления сервисом и его полезная часть (та, что выполняет работу) в нашем случае будет находится в одном и том же файле. Наш сервис может получать следующие команды:
  • install
  • uninstall
  • start
  • stop
при выполнении этих команд передаётся сообщение указанному сервису и дальше работа скрипта прерывается.  Есть ещё один аргумент:
  • run
он сообщает что сервис запущен и надо что-то делать. При получении "run" скрипт начинает выполнять свою полезную часть.
define('NL', "\n\n");
define('DNL', NL . NL);


if (!isset($argv[1]))
{
 echo 'no argument' . DNL;
 exit;
}


switch ($argv[1])
{
 case 'install':

  $result = win32_create_service(array(
       'service' => $service_name,
       'display' => $display_name,
       'params' => '-f ' . __FILE__ . ' run',
       'path'  => 'php',
       ));
  var_dump($result);
  echo 'Service Installed' . DNL;
  exit;

 case 'uninstall':

  $result = win32_delete_service($service_name);
  var_dump($result);
  echo 'Service Removed' . DNL;
  exit;

 case 'start':

  $result = win32_start_service($service_name);
  var_dump($result);
  echo 'Service Started' . DNL;
  exit;

 case 'stop':

  $result = win32_stop_service($service_name);
  var_dump($result);
  echo 'Service Stopped' . DNL;
  exit;

 case 'run':

  break;

 default:

  echo 'unknown argument' . DNL;
  exit();
}
Описание функций, параметров и возвращаемых значений можно посмотреть в документации к PHP.

Всё что нужно сделать во второй части это
  1. Зарегистрироваться как сервис.
  2. Выполнять свой код, периодически проверяя поступившие сообщения.
  3. Отвечать на полученные сообщения.
Мой скрипт будет обрабатывать только два сообщения: запрос на статус и остановка.
set_time_limit(0);


if (!win32_start_service_ctrl_dispatcher($service_name))
 exit('Cannot register script as service: "' . $service_name . '"');

win32_set_service_status(WIN32_SERVICE_RUNNING);


// main loop
while (1)
{
 // Handle Windows Service Request
 switch (win32_get_last_control_message())
 {
  case WIN32_SERVICE_CONTROL_INTERROGATE: // Report its current status to the SCM

   win32_set_service_status(WIN32_NO_ERROR);
   break;

  case WIN32_SERVICE_CONTROL_STOP: // Service should stop

   win32_set_service_status(WIN32_SERVICE_STOPPED);
   break(2);

  default:

   win32_set_service_status(WIN32_ERROR_CALL_NOT_IMPLEMENTED);
 }


 // Полезный код сервиса
 // ...
 // ...
}


// set appropriate status
win32_set_service_status(WIN32_SERVICE_STOPPED);
Это весь код. Дополнительно можно обрабатывать сообщения "Пауза", "Продолжение" (снятие паузы), "Выключение" (ОС).

Установка, работа

Добавляю запись в лог для проверки работы. Результат полностью:
$service_name = 'win_service';
$display_name = 'Test windows service';


define('NL', "\n\n");
define('DNL', NL . NL);


function dumpToFile($variable)
{
 $sep = "\r\n";
 $handle = fopen('d:\\Dev\\htdocs\\win_service\\log.log', 'a+');

 fwrite($handle, '[' . date('H:i:s') . '] ');

 ob_start();
 print_r($variable);
 $printed = ob_get_clean();

 fwrite($handle, $printed . $sep);
 fclose($handle);
}


if (!isset($argv[1]))
{
 echo 'no argument' . DNL;
 exit;
}


switch ($argv[1])
{
 case 'install':

  $result = win32_create_service(array(
           'service' => $service_name,
           'display' => $display_name,
           'params' => '-f ' . __FILE__ . ' run',
           'path'  => 'php',
           ));
  var_dump($result);
  echo 'Service Installed' . DNL;
  exit;

 case 'uninstall':

  $result = win32_delete_service($service_name);
  var_dump($result);
  echo 'Service Removed' . DNL;
  exit;

 case 'start':

  $result = win32_start_service($service_name);
  var_dump($result);
  echo 'Service Started' . DNL;
  exit;

 case 'stop':

  $result = win32_stop_service($service_name);
  var_dump($result);
  echo 'Service Stopped' . DNL;
  exit;

 case 'run':

  break;

 default:

  echo 'unknown argument' . DNL;
  exit();
}


set_time_limit(0);


if (!win32_start_service_ctrl_dispatcher($service_name))
 exit('Cannot register script as service: "' . $service_name . '"');

win32_set_service_status(WIN32_SERVICE_RUNNING);


// main loop
while (1)
{
 // Handle Windows Service Request
 switch (win32_get_last_control_message())
 {
  case WIN32_SERVICE_CONTROL_INTERROGATE: // Report its current status to the SCM

   win32_set_service_status(WIN32_NO_ERROR);
   break;

  case WIN32_SERVICE_CONTROL_STOP: // Service should stop

   win32_set_service_status(WIN32_SERVICE_STOPPED);
   break(2);

  default:

   win32_set_service_status(WIN32_ERROR_CALL_NOT_IMPLEMENTED);
 }


 // Полезный код сервиса
 // ...
 // ...

 dumpToFile('working'); sleep(20);
}


// set appropriate status
win32_set_service_status(WIN32_SERVICE_STOPPED);
Команды управления сервисом выглядят так:

1. установка
php -f d:\Dev\htdocs\win_service\service.php install

2. запуск
php -f d:\Dev\htdocs\win_service\service.php start

3. остановка
php -f d:\Dev\htdocs\win_service\service.php stop

4. удаление
php -f d:\Dev\htdocs\win_service\service.php uninstall

Также нашим сервисом можно управлять через Control Panel -> Administrative Tools -> Services. Там же можно установить тип запуска (Автоматический / Вручную).

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

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