Уведомления

Группа в Telegram: @pythonsu

#1 Апрель 28, 2008 16:50:02

Saff
От:
Зарегистрирован: 2008-03-18
Сообщения: 56
Репутация: +  0  -
Профиль   Отправить e-mail  

Реализации демона

Пишу на питоне недавно, и тут встала задача написать демон(разработка ведётся пока под линукс).
Поизучал потоки и прочее.В инете довольно много разных реализаций.

Стоит задача написать демон, который будет раз в какое то время запускать некоторые задачи + например по сокетному коннекту запускать их принудительно(пока не реализовывал).
Хотел поинтересоваться на правильном ли я пути.
Пока реализовал таймеры примерно так(на примере открытия файла раз в какой то интервал):

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)

Офлайн

#2 Апрель 28, 2008 18:37:00

ZAN
От:
Зарегистрирован: 2007-06-10
Сообщения: 403
Репутация: +  10  -
Профиль   Отправить e-mail  

Реализации демона

Хм… в методе 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)

Офлайн

#3 Апрель 28, 2008 21:30:52

Saff
От:
Зарегистрирован: 2008-03-18
Сообщения: 56
Репутация: +  0  -
Профиль   Отправить e-mail  

Реализации демона

То есть создавать “задания” как отдельные классы со стандартными методами, а так же метод который будет их запускать в основном классе?И если таймер будет работать отдельно, то как он будет запускать нужные мне задания?Просто я не совсем понял, как мне добиться того что бы приложение не завершалось?
Был бы очень благодарен, если бы ты описал небольшой пример.

P.S. спасибо за мелочь, теперь буду юзать именно так )



Офлайн

#4 Апрель 29, 2008 00:03:16

ZZZ
От: Москва
Зарегистрирован: 2008-04-03
Сообщения: 2161
Репутация: +  26  -
Профиль   Адрес электронной почты  

Реализации демона

Хм… Интересная мысль…
А может лучше так:


def run(self):
self.setDaemon(False)
import time
while True: ## Бесконечный цикл. Выход не трудно организовать.
time.sleep(file_interval) ## Ждём file_interval секунд
self.check_file()

def check_file(self):
pass ## Работаем



Офлайн

#5 Апрель 29, 2008 03:50:20

Андрей Светлов
От:
Зарегистрирован: 2007-05-15
Сообщения: 3137
Репутация: +  14  -
Профиль   Адрес электронной почты  

Реализации демона

Каждый понимает под словом “демон” что-то свое, личное. Это обсуждение наглядно данный тезис иллюстрирует.

Для меня демон это:
- в первую очередь процесс, отвязанный от терминала. Т.е. имеющий свой личный 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.
Если есть неточности - не пинайте. Полгода к линуксу не прикасался, сейчас работаю над виндовым проектом. Кое-что мог забыть.



Офлайн

#6 Апрель 29, 2008 09:48:46

Saff
От:
Зарегистрирован: 2008-03-18
Сообщения: 56
Репутация: +  0  -
Профиль   Отправить e-mail  

Реализации демона

Определение самое верное, но с ходу я всё не реализую правильно из за неопытности. Сейчас я хочу добиться того что бы был именно демон в твоём понимании.Ещё не совсем освоился с тем, как заставить работать его “вечно”, что бы он не закрывался, а работал с несколькими настроенными таймерами и открытым сокетным соединением для управления им.Не могу доконца понять принцып(((К сожалению опыта нет в разработке подобных систем.Было бы отлично посмотреть исходник или его часть, что бы понять к чему стремиться.



Офлайн

#7 Апрель 29, 2008 10:06:52

slivlen
От:
Зарегистрирован: 2006-07-06
Сообщения: 764
Репутация: +  0  -
Профиль   Отправить e-mail  

Реализации демона

Saff
К сожалению опыта нет в разработке подобных систем.Было бы отлично посмотреть исходник или его часть, что бы понять к чему стремиться.
Ну вот пример небольшой заготовки для создания демонов. Запускается демон, вешает обработчик сигнала для удаления пида по завершению и создает пид-файл.
P.S. Пример очень не полный, но основную идею думаю отражает =)
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



Офлайн

#8 Апрель 29, 2008 11:38:04

Saff
От:
Зарегистрирован: 2008-03-18
Сообщения: 56
Репутация: +  0  -
Профиль   Отправить e-mail  

Реализации демона

Попробывал зделать по подоюию ))))Точнее почти копипастом, но всё же пытаюсь разобраться…
Получилось что то вроде:
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)

Офлайн

#9 Апрель 29, 2008 13:01:19

ZAN
От:
Зарегистрирован: 2007-06-10
Сообщения: 403
Репутация: +  10  -
Профиль   Отправить e-mail  

Реализации демона

Saff
Был бы очень благодарен, если бы ты описал небольшой пример.
import threading
import time
import timeit

class SocketDaemon(object):
def start(self, launcher):
self.launcher = launcher
threading.Thread(target=self.handleConn).start()

def handleConn(self):
while 1:
‘'’handle socket
if tralala:
self.launcher.launch('helloWorld')
‘'’


class TimerDaemon(object):
def start(self, launcher):
self.launcher = launcher
threading.Thread(target=self.handleTime).start()

def handleTime(self):
timeit.default_timer()
while 1:
time.sleep(5)
self.launcher.launch('helloWorld')
print ‘total time %s\n’ %timeit.default_timer()


class Launcher(object):
def __init__(self):
self.taskDict = {}

def addTask(self, taskName, task):
if hasattr(task, ‘execute’):
self.taskDict = task
else:
raise Exception('object %s is not executable' %task)

def launch(self, taskName):
threading.Thread(target=self.taskDict.execute).start()


class BaseTask(object):
def execute(self):
pass


class HelloWorldTask(BaseTask):
def execute(self):
print ‘Hello, world!’


if __name__ == ‘__main__’:
task = HelloWorldTask()
launcher = Launcher()
launcher.addTask('helloWorld', task)
timerDaemon = TimerDaemon()
timerDaemon.start(launcher)


Hello, world!
total time 4.99955050153

Hello, world!
total time 9.99956726344

Для того, чтобы приложение не завершалось, нужен бесконечный цикл. Такой же цикл нужен для демона, слушающего сокет. Одновременно в одном потоке, очевидно, два таких цикла реализовать нельзя. Чтобы не совмещать обработку события наступления времени с обработкой соединения в одном и том же цикле, намного проще вынести эти процессы в отдельные потоки.
В примере - таймер и сокет сервер - это посылатели сообщений (хотя фактически, они просто вызывают метод launch с параметром), launcher - слушатель. Он хранит в себе задания и запускает их отдельным потоком.
Ну а как именно реализовать сокет-демон, пример уже тоже есть :)



Отредактировано (Апрель 29, 2008 13:53:11)

Офлайн

#10 Апрель 29, 2008 14:59:52

Saff
От:
Зарегистрирован: 2008-03-18
Сообщения: 56
Репутация: +  0  -
Профиль   Отправить e-mail  

Реализации демона

Все примеры отличные!Попробую реализовать как ZAN, ибо кажется что не получилось(а может не совсем понял пример) slivlen'a, т.к приложение почему то при запуске дополнительных потоков отображается в листе процессов как зомби((
Хотя всё равно попробую исполнить в 2х вариантах, от более простого к сложному с логами stdout'ом и прочим.
Если есть ещё какие то советы или ссылки на хорошие мануалы или просто статьи по данной теме, был бы очень благодарен!
P.S. после php сложновато понять такую динамику )))



Офлайн

Board footer

Модераторировать

Powered by DjangoBB

Lo-Fi Version