Bash daemon, демон на bash



В общем история такая. Было мне задание написать демона для «форка» или просто клонирования определенных скриптов, да так, чтоб можно было управлять количеством одновременно запущенных процессов – что-то вроде универсального демона. Идея очень интересная! Позволяет не думать более о форках в php или чем-то другом. Принципиально позволяет отделить эту реализацию от ваших программ или скриптов, будь то php, C, C++, bash.
Плюсом ко всему этому нужно было как-то сообщать скрипту, который должен будет клонироваться какие его копии еще работают, для этого придется искать pid копиий «команды».

Для реализаци был выбран bash, т.к. он скриптовой и наиболее удобен всем, да и разобраться в нем не сложно.

1. Создаем файл, например clone_daemon.sh

2. Ставим ему права на исполнение

3. Копируем в него этот код:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
#!/bin/bash
PROCESSPID=$$

showhelp () {
echo "
Usage:  $0 [PAREMETRS]  Run the specified command
    $0 --help       Display this help and exit
    $0 --version        Output version information and exit

Parametrs:
    -с - Command to run
    -n - Number of simultaneously running command clones. Must be integer and greater than zero
    -s - Timeout for the eternal loop. Must be integer and greater than zero
    -l - For the eternal loop
"

    exit $?
}

case $1 in
    "" | "--help" | "--help" | "--version" | "-version") showhelp;;
esac


# Вечный цикл
loopWhile=0
# Время задержки в секундах
sleepTime=null
# Лимит одновременно запущенных процессов
countCloneLimit=0

# Получаем параметры
# Очень хорошо написано про них тут http://www.ibm.com/developerworks/ru/library/l-bash-parameters/index.html
while getopts ":c:n:s:l" optname
do
    case "$optname" in
    "l")
        loopWhile=1;;
    "s")
        sleepTime=$OPTARG;;
    "n")
        countCloneLimit=$OPTARG;;
    "c")
        command=$OPTARG;;
    "?")
        echo "Unknown option -$OPTARG"
        exit 1
        ;;
    ":")
        echo "No argument value for option -$OPTARG. Run '$0 --help'"
        exit 1
        ;;
    esac
done

# Проверяем команду на выполнение. Там обязательно должны быть буквы
if [ `echo $command | grep -c [^0-9]` = "0" ]
then
    echo "Wrong command. '$command'. Run '$0 --help'"
    exit 1
fi

# sleepTime должен быть числом и > 0
if [ "$sleepTime" != "null" ] && [ `echo $sleepTime | grep -c [^0-9]` = "1" ] || [ "$sleepTime" = "0" ]
then
    echo "  Invalid parameter [-s]. Run '$0 --help'"
    exit 1
fi

# countCloneLimit должен быть числом и > 0
if [ `echo "$countCloneLimit" | grep -c [^0-9]` = "1" ] || [ $countCloneLimit = "0" ]
then
    echo "  Invalid parameter [-c]. Run '$0 --help'"
    exit 1
fi

PIDFILESDIR=/tmp
PIDFILENAME=`echo $0 $command | md5sum`.pid
PIDFILEFULL=$PIDFILESDIR/$PIDFILENAME

# Проверяем существует ли файл pid для ткого процесса
if [ `ls "$PIDFILESDIR" | grep "$PIDFILENAME" | grep -c -v grep` = "1" ]
then
    pid=`cat "$PIDFILEFULL"`
    # Проверяем если ли такой процесс в памяти, если нет, то удаляем файл
    if [ `ps -p $pid | grep $pid | grep -c -v grep` = "0" ]
    then
        `rm "$PIDFILEFULL"`
    else
        echo "Process with this command is already running. exit..."    
        exit 1
    fi
fi

# Пишем pid процесса в файл
`echo "$PROCESSPID" > "$PIDFILEFULL"`

# Экранируем $command
escapeCommand=`echo "$command" | sed -e "s/\//\\\\\\\\\\//g" | sed "s/'/'\"'\"'/g"`

# Делаем бесконечный цикл
while(true)
do
    countClone=0
    # Получаем список pid запущенных процессов "$command"
    pidList=`ps ax -o "%p %a" | grep "$command" | grep -v "$0" | grep -v grep | sed "s/$escapeCommand//I" | grep -v 'sed s///I' | awk '{print $1,"$command"}'`
   
    # Если в $pidList есть цифры, значит процессы есть и значит, мы их считаем
    if [ `echo $pidList | grep -c [0-9]` \> 0 ]
    then
        # Считаем общее количество процессов
        countClone=`echo "$pidList" | wc -l`
        echo $countClone
    fi

    # Считаем сколько процессов нужно запустить
    countNeed=`expr "$countCloneLimit" - "$countClone"`
    pidString=`echo $pidList | sed -e "s/[^ 0-9]//g"`
    for i in $(seq 1 $countNeed);
    do
        eval "nohup $command $pidString >> /dev/null&"
    done

    # Если не установлен флаг вечного цикла, то выходим из зацикливания
    if [ "$loopWhile" = "0" ]
    then
        exit $?
    fi

    # Засыпаем, чтобы не делать бесконечный цикл
    sleep $sleepTime
done

exit $?

4. Запускаем с параметрами:

  • [-c] команда на выполнение в кавычках
  • [-n] максимальное количество одновременно запущенных процессов
  • [-s] Время торможения в цикле while, т.е. через какой промежуток времени в секундах надо проверить количество экземпляров «команды» [-c], и запустить количество недостающих
  • [-l] Говорит о том, что нужен «вечный цикл»

Например:

1
$ ./clone_daemon -c "php script.php" -s 1 -n 5 -l

Даст вам 5 одновременно работающих клонов команды [-c] в бесконечном цикле [-l] с задержкой [-s] между новым проходом

Если же вам надо просто единоразово запустить несколько клонов в фоновом режиме, то следующая команда для вас:

1
$ ./clone_daemon -c "php script.php" -n 5

Это даст вам 5 одновременно работающих клонов команды [-c]

Для языка фонового скрипта был выбран php. Там мы и будем разбирать pidList.

Пример:

1
2
3
4
5
6
7
8
9
10
11
<?php
    $pidArray = array();
    if (isset($argv[1]))
    {
        foreach (explode("\n", $argv[1]) as $pid)
        {
            $pidArray[] = trim($pid);
        }
    }
    print_r ($pidArray);
?>

Так мы можем узнать какие скрипты у нас вылетели, сделать стату или просто защититься от сбоя в скрипте и перезапустить его.

Похожие статьи:

  1. nginx proxy + apache2 + bash
  1. А процессорное время не отжирает в бесконечном цикле???
    Я обычно такие вещи делаю по крону раз в 2-5 минут…

  2. Ну тогда по проще :) ) но всё равно – зачем заморачиваться со своим циклом, если есть крон?

    • Самое главное, для чего нужен данный скрипт – постоянное удерживание того количества одновременно выполняемых экземпляров команд (приложений), которое было указано. С проверкой раз в [-s] секунд.

      Например, если нужен парсер постоянно работающий в 5 экземплярах. Т.е. каждый дочерний скрипт может быть настолько атомарным, насколько можно – допустим парсит один урл, разбирает содержимое и умирает. На работе мы теперь успешно применяем этот bash скрипт для многих задач.

Ответить


[ Ctrl + Enter ]