Форум сайта python.su
Пишу на питоне недавно, и тут встала задача написать демон(разработка ведётся пока под линукс).
Поизучал потоки и прочее.В инете довольно много разных реализаций.
Стоит задача написать демон, который будет раз в какое то время запускать некоторые задачи + например по сокетному коннекту запускать их принудительно(пока не реализовывал).
Хотел поинтересоваться на правильном ли я пути.
Пока реализовал таймеры примерно так(на примере открытия файла раз в какой то интервал):
from threading import Timer
import threading
import time
class MyDaemon(threading.Thread):
flag_run = 1
cnt_time = 0
file_check = 0
file_interval = 10000
def run(self):
self.setDaemon(False)
self.file_check = Timer(file_interval,self.check_file)
self.file_check.start()
def check_file(self):
if self.flag_run==1:
handle = open(“test_file.txt”)
if handle:
content = “”.join()
else:
print “file not found”
self.file_check = Timer(file_interval,self.check_file)
self.file_check.start()
daemon = MyDaemon()
daemon.start()
print ‘Daemon started…..’
В данном варианте он будет работать вечно.Но вопрос в том, “правильная” ли это реализация для данной задачи?
Т.к остальные действия я пока хочу делать по похожей схеме.
Ну и + меня смутило что постоянно меняется PID у процесса…
Жду мнения професионалов.
Отредактировано (Апрель 30, 2008 09:42:24)
Офлайн
Хм… в методе run создается ждущий тред. По истечению определенного времени он читает файл (понимать - выполняет задание), а затем в конце процесса создает копию себя. Это означает, что процесс отвалится после максимального количества рекурсий.
Я бы разделил систему на посылателей сигнала и сигнало-получателя, который и будет лаунчить задания.
Схема примерно такая - класс Launcher хранит список заданий (простых объектов с методом execute), которые могут быть им запущены в отдельном треде. Этот же класс является слушателем сообщений от:
1. Демона-сокета
2. Таймера
В сообщении будет только имя задания, которое нужно запустить.
И еще одна мелочь - handle = open(“test_file.txt”)
if handle:
content = “”.join()
else:
print “file not found”
Во-первых, если файл не будет найден, то выпадет эксэпшн, а во-вторых, прочесть все его содержимое можно contents = handle.read()
Отредактировано (Апрель 28, 2008 18:45:28)
Офлайн
То есть создавать “задания” как отдельные классы со стандартными методами, а так же метод который будет их запускать в основном классе?И если таймер будет работать отдельно, то как он будет запускать нужные мне задания?Просто я не совсем понял, как мне добиться того что бы приложение не завершалось?
Был бы очень благодарен, если бы ты описал небольшой пример.
P.S. спасибо за мелочь, теперь буду юзать именно так )
Офлайн
Хм… Интересная мысль…
А может лучше так:
…
def run(self):
self.setDaemon(False)
import time
while True: ## Бесконечный цикл. Выход не трудно организовать.
time.sleep(file_interval) ## Ждём file_interval секунд
self.check_file()
def check_file(self):
pass ## Работаем
…
Офлайн
Каждый понимает под словом “демон” что-то свое, личное. Это обсуждение наглядно данный тезис иллюстрирует.
Для меня демон это:
- в первую очередь процесс, отвязанный от терминала. Т.е. имеющий свой личный sid. Делается через fork. И закрывший свои stdin, stdout, stderr. Можно добавить еще что-нибудь по вкусу.
- умеющий определять, только ли один демон запущен в системе, и не позволять запуск второго. (И отмечаться при этом где то в /var/pid).
- умеющий хорошо завершиться по сигналу “сгинь, зараза”. kill -1 <pid>, например.
- умеющий поменять process name на свое - очень неудобно видеть в ps -afx множество python, выделяя из них мой демон “по походке”. Решается через os.exec.
- умеющий писать логи в файл (файл должен быть где-то в /var/log)
- позволяющий быстро написать скрипт для /etc/init.d для автоматического запуска/завершения (естественно, прийдется размножить этот скрипт по runlevel. В разных linux это решается по своему, но требования к демону общие)
- делающий что-нибудь еще, для чего, собственно, и предназначен.
Есть два способа: взять готовое решение и изобрести велосипед.
- Для меня готовое решение - twisted. Может быть, мощнее чем нужно - но замечательно работает. А еще я twisted очень люблю…
- Полгода назад пришлось изобрести таки велосипед - для одной мелкой задачки, уже имеющей решение но не умеющей быть демоном.
Если интересно - могу выложить код. Не уверен в его полной корректности и адекватности. Что-то, помню, правил “на лету” на сервере.
Кода - чуть, но он не очень-то подготовлен для повторного использования. Задача не стояла.
Что-то могло остаться недотестированным. Проблема была решена, и я о ней забыл.
P.S.
Если есть неточности - не пинайте. Полгода к линуксу не прикасался, сейчас работаю над виндовым проектом. Кое-что мог забыть.
Офлайн
Определение самое верное, но с ходу я всё не реализую правильно из за неопытности. Сейчас я хочу добиться того что бы был именно демон в твоём понимании.Ещё не совсем освоился с тем, как заставить работать его “вечно”, что бы он не закрывался, а работал с несколькими настроенными таймерами и открытым сокетным соединением для управления им.Не могу доконца понять принцып(((К сожалению опыта нет в разработке подобных систем.Было бы отлично посмотреть исходник или его часть, что бы понять к чему стремиться.
Офлайн
SaffНу вот пример небольшой заготовки для создания демонов. Запускается демон, вешает обработчик сигнала для удаления пида по завершению и создает пид-файл.
К сожалению опыта нет в разработке подобных систем.Было бы отлично посмотреть исходник или его часть, что бы понять к чему стремиться.
import os, sys import signal import inspect def sig_handler(*args, **kwargs): if len(args) > 0 and inspect.isfunction(args[0]): usr_handler = args[0] args = args[1:] def res_handler(sing_num, frame): usr_handler(*args, **kwargs) return res_handler else: def tmp_handler(handler_func): return sig_handler(handler_func, *args, **kwargs) return tmp_handler def create_pidfile(pidfile): @sig_handler(pidfile) def delete_pidfile(pidfile): os.unlink(pidfile) sys.exit(0) open(pidfile, 'w').write(str(os.getpid())) signal.signal(signal.SIGTERM, delete_pidfile) def daemonize(pidfile=None): if os.fork(): sys.exit(0) os.setsid() if os.fork(): sys.exit() if pidfile: create_pidfile(pidfile) if __name__ == '__main__': daemonize('your.pid') # Daemon`s job
Офлайн
Попробывал зделать по подоюию ))))Точнее почти копипастом, но всё же пытаюсь разобраться…
Получилось что то вроде:
import os, sys
import signal
import inspect
from threading import Timer
import threading
import time
import MySQLdb
class Launcher(threading.Thread):
#CHeck File
class fileCheck(threading.Thread):
def execute(self):
self.flag_run = 0
handle = open(“test_file.txt”)
if handle:
a = handle.read()
print a
def run(self):
action = “filecheck”
self.setDaemon(False)
print “RUN”
if action==“filecheck”:
event_action = self.fileCheck()
event_action.execute()
class DaemonThread(threading.Thread):
flag_run = 1
cnt_time = 0
file_check = 0
def run(self):
self.setDaemon(False)
self.daemonize('saff.pid')
run_event = Launcher(self)
run_event.start()
time.sleep(50)
def daemonize(self,pidfile=None):
if os.fork():
sys.exit(0)
os.setsid()
if os.fork():
sys.exit()
if pidfile:
self.create_pidfile(pidfile)
def create_pidfile(self,pidfile):
self.sig_handler(pidfile)
open(pidfile, ‘w’).write(str(os.getpid()))
signal.signal(signal.SIGTERM, self.delete_pidfile)
def delete_pidfile(self,pidfile,tmp):
os.unlink(pidfile)
sys.exit(0)
def sig_handler(*args, **kwargs):
if len(args) > 0 and inspect.isfunction(args):
usr_handler = args
args = args
def res_handler(sing_num, frame):
usr_handler(*args, **kwargs)
return res_handler
else:
def tmp_handler(handler_func):
return sig_handler(handler_func, *args, **kwargs)
return tmp_handler
if __name__ == ‘__main__’:
daemon = DaemonThread()
daemon.setName(“myDaemon”)
daemon.start()
if daemon.isAlive():
print “I'm ALIVE!!”
Но при SIGTERM'e ошибка:
Exception in thread myDaemon:Ну и + всё равно не совсем понимаю, как реализовать неприрывную его работу (((( по нескольким таймерам…
Traceback (most recent call last):
File “/usr/lib/python2.5/threading.py”, line 460, in __bootstrap
self.run()
File “test.py”, line 47, in run
time.sleep(50)
File “test.py”, line 68, in delete_pidfile
os.unlink(pidfile)
TypeError: coercing to Unicode: need string or buffer, int found
Отредактировано (Апрель 30, 2008 09:41:56)
Офлайн
Saffimport threading
Был бы очень благодарен, если бы ты описал небольшой пример.
Отредактировано (Апрель 29, 2008 13:53:11)
Офлайн
Все примеры отличные!Попробую реализовать как ZAN, ибо кажется что не получилось(а может не совсем понял пример) slivlen'a, т.к приложение почему то при запуске дополнительных потоков отображается в листе процессов как зомби((
Хотя всё равно попробую исполнить в 2х вариантах, от более простого к сложному с логами stdout'ом и прочим.
Если есть ещё какие то советы или ссылки на хорошие мануалы или просто статьи по данной теме, был бы очень благодарен!
P.S. после php сложновато понять такую динамику )))
Офлайн