Как сделать программу демон

Вы имеете ввиду книгу авторов (Марк Митчел, Джеффри Оулдем, Алекс Самьюэл) ну так это пародия на язык. Те темы, которые авторы понимают описаны отлично, но где они сомневаются ограничено общими словами.

а что читать новичкам?

PostgresPro

Inferno Solutions

Закладки на сайте
Проследить за страницей
Created 1996-2023 by Maxim Chirkov
Добавить, Поддержать, Вебмастеру

Источник: www.opennet.ru

Создание демона на PHP или скрипты, которые работают сами по себе

При разработке веб-приложений может возникнуть потребность в том, чтобы сервер что-то делал самостоятельно, без участия пользователя: занимался постобработкой данных, делал рассылки, отслеживал что-то, обрабатывал. Когда речь заходит о самостоятельной работе сервера, первое, что приходит в голову вебмастеру, привыкшему работать c Unix-подобными серверами, это программа Cron, которая может выполнять команды по расписанию. Но как быть, если не существует никакого расписания, и какой-то скрипт должен быть запущен постоянно и работать сам по себе, запускаясь заново сразу после своего выполнения? Вдохновлённый разработкой сервиса CheckTrust, вообразив себя Некромантом, я решил поделиться своим опытом в создании таких скриптов-демонов на PHP.

КАК ИСПРАВИТЬ ОШИБКУ ДЕМОНА?ОТВЕТ ЗДЕСЬ! |Game Guardian|

PHP Демон

Сразу оговорюсь, что всё, о чём будет идти речь в этой статье, применимо лишь для Unix-подобных операционных систем, и не будет работать под Windows. Речь идёт именно об альтернативе крону. Несчастным любителям Windows придётся колдовать с «Планировщиком заданий» — Чёрная Магия останется недоступна для них 🙂 Кроме того, потребуется ssh доступ, а в идеале — доступ php скриптов к командной строке.

Что есть программа-демон и как написать демона на PHP

Де́мон (daemon, dæmon, др.-греч. δαίμων божество) — компьютерная программа в системах класса UNIX, запускаемая самой системой и работающая в фоновом режиме без прямого взаимодействия с пользователем. Демоны обычно запускаются во время загрузки системы.

Применимо к PHP, это скрипт, который может работать самостоятельно, без остановок и без участия пользователя. Как получить такой скрипт? На самом деле, очень просто, нужно лишь нарушить одно из первых правил программирования, которому учат в школе, и создать бесконечный цикл:

// Чтобы программа работала постоянно, она просто должна постоянно работать 😉 while(1) < // Тут будет располагаться код Демона // . // Время сна Демона между итерациями (зависит от потребностей системы) sleep(1); >

Простой до невозможности код вызывает, всё же, несколько вопросов. Как его запустить? Как отслеживать его выполнение? Как его остановить?

Написание демона, часть первая. Создание скрипта для демонизации.

Как запустить php-демона

А как вообще запускают php-скрипты? Если это веб-приложение, то при помощи браузера и веб-сервера. Но этот вариант не подходит, ведь мы имеем дело с бесконечным скриптом, а время выполнения скриптов ограничены директивой max_execution_time в php.ini. Следовательно, бесконечный скрипт необходимо запускать через консоль, ведь тогда максимальное время его выполнения не учитывается. Примерно так выглядит команда запуска демона:

Читайте также:
Выполнение программы ремонта это

php -f /path/to/your/daemon.php

Остановить процесс демона можно так же, как и любой другой процесс:

kill xxxx

В приведённом примере xxxx — это и есть PID, идентификатор процесса.

Стоит отметить, что это не остановка, а именно «убийство» процесса демона. Дело в том, что работа скрипта будет прервана где попало, что не всегда и не всем подходит. По идее, в таком случае демона нужно останавливать где-то между его итерациями и уже не из консоли. К примеру, мы можем создать в базе данных или в каком-то файле на сервере заявку на остановку скрипта, а между итерациями демона проверять, нет ли такой заявки. Если заявка будет обнаружена, остановить цикл оператором break.

Система управления демонами

А что, если требуется создать и отслеживать сразу много демонов? К примеру, в упомянутом выше сервисе CheckTrust обработкой данных и проектов пользователей занимаются > 30 таких скриптов. Создавать и следить за ними из консоли очень неудобно — нужен более дружественный интерфейс.

Система управления демонами - создание демона

Круто, да? 🙂 Как раз для создания подобной системы было бы неплохо иметь доступ к командной строке из php. Каждый демон — запись в базе данных, напротив которой можно проставить команду его запуска, а так же PID процесса. Следовательно, появляется возможность запускать, останавливать и отслеживать статус демонов прямо из веб-интерфейса. В итоге сами демоны у меня получились частью консольного приложения, а система управления ими — частью веб-приложения.

Система управления демонами - список демонов

Поскольку система, показанная выше, заточена строго под CheckTrust, её код показывать я не буду. Зато скопирую сюда код php класса для управления процессами, который я использовал при её создании:

Единственный недостаток этого класса — то, что я уже описывал ваше — он останавливает процессы методом kill, но мне пока что этого достаточно 🙂 А вот и пример его использования:

// Запуск демона и получение PID (предполагается, что pid где-то сохраняется после запуска) $command = »; $process = new Process($command); $processId = $process->getPid(); // Проверка статуса демона $process = new Process(); $process->setPid($processId); $status = $process->status(); // возвращает true или false // Остановка демона $process = new Process(); $process->setPid($processId); $stopped = $process->stop(); // возвращает true или false

Подводя итог, хочу сказать, что это не единственно возможная и, вполне возможно, не самая оптимальная реализация демонов на php. К примеру, для многопроцессовых демонов существует крутое расширение PHP PCNTL. Кто-то, возможно, даже скажет, что для консольных приложений существуют совсем другие языки программирования. Но, как ни крути, у данной реализации есть неоспоримые преимущества:

  • Она простая, как тапок. Демон — это просто бесконечный цикл, куда уж проще.
  • Она совместима с веб-приложениями — являясь частью веб-сервиса, мои демоны опираются на существующие наработки, используют те же модели данных, классы и методы работы с ними.
  • Она работает! Серьёзно — некоторые демоны у нас запущены уже несколько месяцев и, будучи грамотно написанными, не тупят, не зависают, не поглощают память.
Читайте также:
Примеры программ с if

Спасибо за внимание 🙂 Если у кого-нибудь возникнут вопросы или идеи, с удовольствием отвечу на них в комментариях.

Источник: www.mithrandir.ru

Создание демона Python с использованием Systemd

Создание демона Python с использованием Systemd

Недавно у меня возникла задача создать демон (фоновое приложение) реализованный на Python в системе Linux использующей Systemd . В поисках современного решения и родилась данная статья. Ранее для реализации демона выполнялась «демонизация» приложения Python, зачастую с помощью библиотеки python-daemon

(opens new window) . Даже была создана спецификация pep-3143

(opens new window) для реализации демонов. Но на текущий момент времени с использованием Systemd нет необходимости демонизировать наше Python приложение, достаточно корректно описать его запуск в юните. Для начала рассмотрим как работает Systemd .

Все операции указанные в данной статье выполнялись в окружении Linux Ubuntu 20, с установленным Python 3.8.

# Systemd Unit

  • /usr/lib/systemd/system/ — юниты из установленных пакетов, такие как nginx, postgreee и др.
  • /run/systemd/system — юниты созданные в runtime
  • /etc/systemd/system — юиниты, созданные администратором, в основном пользовательские юниты должны храниться здесь.

Для создания юнита нам необходимо описать 3 секции: [Unit], [Service], [Install] Основные переменные блока [Unit]:

[Unit] Descripiton=Unit Descripion After=syslog.target After=network.target After=nginx.service After=mysql.service Requires=mysql.service Wants=redis.service

  • Description — описание юнита
  • After — указывает, что юнит должен быть запущен после группы указанных сервисов
  • Requires — узказывает, что для запуска юнита требуется запущенный сервис mysql , запуск нашего сервиса выполняется паралллельно с требуемым ( mysql ), если требуемый не указан в After
  • Wants — описательная переменная, показывающая, что для запуска сервиса желателен запущенный сервис redis

[Service] Type=simple PIDFile=/var/lib/service.pid WorkingDirectory=/var/www/myapp User=user Group=user Environment=STAGE_ENV=production OOMScoreAdjust=-100 ExecStart=/my_venv/bin/python my_app.py —start ExecStop=/my_venv/bin/python my_app.py —stop ExecReload=/my_venv/bin/python my_app.py —restart TimeoutSec=300 Restart=always

  • Type — тип запуска: simple (по умолчанию) запускает службу незамедлительно при этом процесс не должен разветвляться, не подходит если другие службы зависят от очередности при запуске данной службы; forking — служба запускается однократно и процесс разветвляется с завершением родительского процесса; другие типы можно рассмотреть по ссылке ниже.
  • PIDFile — позволяет задать место нахождения pid файла
  • WorkingDirectory — указывает рабочий каталог приложения, если указан то ExecStart|Stop|Reload запускаются из этого каталога, т.е. my_app.py станет /var/www/myapp/my_app.py
  • User, Group — соответственно пользователь и группа под которыми будет запущен сервис.
  • Environment — переменные окружения
  • OOOMSCoreAdjust — запрет на kill сервиса вследствие нехватки памяти и срабатывания механизма ООМ: -1000 полный запрет, -100 понижает вероятность.
  • ExecStart|Stop|Reload — команды запуска, останова, перезагрузки сервиса, команда должна использовать абсолютный путь к исполняемому файлу.
  • Timeout — время ожидания systemd отработки команд Start|Stop в сек.
  • Restart — перезапуск сервиса если он упадет.

[Install] WantedBy=multi-user.target

  • WantedBy — уровень запуска нашего сервиса, mulit-user.target или runlevel3.target соответствует runlevel=3 «Многопользовательский режим без графики»

Размещаем данный файл с указанными секциями в директории /etc/systemd/sysmtem/.service

Проверяем его статус:

systemctl -l status test_unit

Разрешаем его запуск:

systemd enable test_unit

Запускаем наш сервис

systemctl start test_unit

При внесении изменений в наш сервис перегружаем его:

systemctl daemon-reload

# Python приложение

Разобрав как работает Systemd можем приступить к созданию нашего сервиса. Для ознакомления создадим приложение Python которое будет выводить сообщения в log файл test_daemon.py :

Читайте также:
Подключение к рабочему столу через интернет программа

import time import argparse import logging logger = logging.getLogger(‘test_daemon’) logger.setLevel(logging.INFO) formatstr = ‘%(asctime)s — %(name)s — %(levelname)s — %(message)s’ formatter = logging.Formatter(formatstr) def do_something(): «»» Здесь мы только лишь пишем сообщение в Log, но можем реализовать абсолютно любые задачи выполняемые в фоне. «»» while True: logger.info(«this is an INFO message») time.sleep(5) if __name__ == «__main__»: parser = argparse.ArgumentParser(description=»Example daemon in Python») # Мы можем заменить default или запускать приложение с указанием нахождения # log файла, через параметр -l /путь_к_файлу/файл.log parser.add_argument(‘-l’, ‘—log-file’, default=’/home/user/test_daemon.log’) args = parser.parse_args() fh = logging.FileHandler(args.log_file) fh.setLevel(logging.INFO) fh.setFormatter(formatter) logger.addHandler(fh) do_something()

Проверим работоспособность нашего скрипта:

python test_daemon.py

Проверим что log-файл создан и в него пишутся сообщения:

tail -f /home/user/test_daemon.log
[Unit] Description=Test daemon After=syslog.target [Service] Type=simple User=user Group=user WorkingDirectory=/home/user/ ExecStart=/usr/bin/python3 test_daemon.py [Install] WantedBy=multi-user.target

В данном юните следует изменить пользователя и группу User , Group на вашего пользователя. Также указать в качестве рабочего каталога WorkingDirectory абсолютный путь к директории где находится файл test_daemon.py.

В случае использования venv в параметре ExecStart следует указать путь к python внутри venv , т.е. заменть /usr/bin/python3 на /venv/bin/python

Проверяем статус нашего сервиса:

systemctl -l status test_daemon

Разрешаем его запуск:

systemd enable test_daemon

Запускаем наш сервис

systemctl start test_daemon

Проверим отображение данных в логе:

tail -f /home/user/test_daemon.log

Наш демон заработал, но осталась не решенной еще одна задача. Зачастую при остановке нашего демона требуется выполнить каие либо задачи (сохранить состояние приложения, отправить уведомление, выполнить очистку данных и т.п.), но на текущий момент при останове наш демон просто закрывается.

# Обработка сигналов завершения

В стандартной библиотеке Pyhton реализован модуль sygnal позволяющий обрабатывать сигналы UNIX-based операционной системы. Полный перечень сигналов можно посмотреть по команде:

kill — l

Демон Systemd при выполнении операции останова демона отправляет изначально сигнал 15 (SIGTERM) — нормальный останов процесса. По умолчанию если в течение 30 сек. не будет получен сигнал выхода приложения будет отправлен сигнал жесткого завершения процесса 9 (SIGKILL) .

Таким образом в нашем приложении необходимо предусмотреть обработчик сигнала 15 (SIGTERM) , как результат наш скрипт test_daemon.py примет следующий вид:

import time import argparse import logging import sys import signal logger = logging.getLogger(‘test_daemon’) logger.setLevel(logging.INFO) formatstr = ‘%(asctime)s — %(name)s — %(levelname)s — %(message)s’ formatter = logging.Formatter(formatstr) def terminate(signalNumber, frame): «»» Здесь мы можем обработать завершение нашего приложения Главное не забыть в конце выполнить выход sys.exit() «»» logger.info(f’Recieved signalNumber>’) sys.exit() def do_something(): «»» Здесь мы только лишь пишем сообщение в Log, но можем реализовать абсолютно любые задачи выполняемые в фоне. «»» while True: logger.info(«this is an INFO message») time.sleep(5) if __name__ == «__main__»: parser = argparse.ArgumentParser(description=»Example daemon in Python») # Мы можем заменить default или запускать приложение с указанием нахождения # log файла, через параметр -l /путь_к_файлу/файл.log parser.add_argument(‘-l’, ‘—log-file’, default=’/home/user/test_daemon.log’) args = parser.parse_args() signal.signal(signal.SIGTERM, terminate) fh = logging.FileHandler(args.log_file) fh.setLevel(logging.INFO) fh.setFormatter(formatter) logger.addHandler(fh) do_something()

Теперь при останове нашего демона:

systemctl start test_daemon

Рейтинг
( Пока оценок нет )
Загрузка ...
EFT-Soft.ru