高效复制:在Linux中并发执行相同任务,各自生成日志

326 阅读2分钟

业务爸爸的需求

需求:有个跑批任务 /data/wwwroot/cmd/OnlyOnceController.php,需要开通n(>=3)个进程同时处理, 同时满足如下条件:
     把日志重向到对应的日志文件如(out_[N].log, N对应进程编号)
     如果任务异常退出,并能拉起。(注意如果进程3拉起后,日志还要继续追加到之前的out_3.log)
为了方便演示,我把有业务逻辑的php 程序,换成一个shell脚本,如下:

root@VM-ubuntu:/opt/tmp# cat tpl.sh 
while true; do
    echo "$(date +'%Y-%m-%d %H:%M:%S')"
    sleep 10s
done

执行任务命令: nohup bash tpl.sh >> out_1.log &

无脑脚本大法

思路如下: 由于重定向是在程序执行时进行的操作,ps 命令无法直接查看程序的重定向信息。所以为了满足上面要求的第二条,我们需要把脚本名设置成唯一的。如tpl_1.sh.
p# cat run.sh 
#!/bin/bash
# author 小毛驴
#

SHELL_DIR=$(cd $(dirname $0);pwd)
cd ${SHELL_DIR}

# 任务数量
NUM_TASKS=3

# 任务命令
TASK_CMD_TPL="tpl.sh"

# 循环监控脚本的运行状态
while truedo
    for ((i=1; i<=$NUM_TASKS; i++)); do
      # 检查任务是否已经在运行
      echo ${TASK_CMD_TPL}
      if ! pgrep -f "${TASK_CMD_TPL}_$i" > /dev/null; then
        if [ ! -e ${TASK_CMD_TPL}_$i ]; then
            cp ${TASK_CMD_TPL} ${TASK_CMD_TPL}_$i
            chmod +x ${TASK_CMD_TPL}_$i
        fi
        nohup bash  ${TASK_CMD_TPL}_$i >> out_$i.log &
        echo "Task ${TASK_CMD_TPL}_$i started."
      else
        echo "Task ${TASK_CMD_TPL}_$i is already running."
      fi
    done
    sleep 10
done
#  nohup  bash run.sh &
# ls
nohup.out  out_1.log  out_2.log  out_3.log  run.sh  tpl.sh  tpl.sh_1  tpl.sh_2  tpl.sh_3
# pgrep -f tpl.sh
15855
16043
16100
# tail -f out*.log
==> out_1.log <==
2023-04-10 22:49:12

==> out_2.log <==
2023-04-10 22:49:12

==> out_3.log <==
2023-04-10 22:49:12
虽然比较low,但是功能也实现。注意为了防止run.sh意外退出,上面的run.sh脚本要放到crontab任务并加一个排它锁,或者称为写入锁。
有没有更好的方式呢? 
那肯定是有的,我们之所以用 tpl.sh_1  tpl.sh_2 这些脚本,就是执行运行多个tpl.sh脚本后,如果其中一个挂掉,重新拉起,不能满足上述要求的第二条。如果每个脚本或者命令执行的时候都有一个唯一id,基于这个id拉起对应的任务,那就解决要复制多个脚本的问题了。

给命令加一个唯一id

command --unique-id xxxx 的方式,给每个脚本或者命令执行的时候指定一个唯一id
p# cat unique-id.sh 
#!/bin/bash
# author 小毛驴
#

SHELL_DIR=$(cd $(dirname $0);pwd)
cd ${SHELL_DIR}

# 任务数量
NUM_TASKS=3

# 任务命令
TASK_CMD_TPL="tpl.sh"

# 循环监控脚本的运行状态
while truedo
    for ((i=1; i<=$NUM_TASKS; i++)); do
      # 检查任务是否已经在运行
      # 此时检查的是 唯一id
      if ! pgrep -f "${TASK_CMD_TPL}_$i" > /dev/null; then
        nohup bash  ${TASK_CMD_TPL}  --unique-id  "${TASK_CMD_TPL}_$i" >> out_$i.log &
        echo "Task ${TASK_CMD_TPL}_$i started."
      else
        echo "Task ${TASK_CMD_TPL}_$i is already running."
      fi
    done
    sleep 10
done
# nohup  bash unique-id.sh &
# ps -ef | grep tpl.sh
root     22689 22675  0 23:03 pts/1    00:00:00 bash tpl.sh --unique-id tpl.sh_1
root     22693 22675  0 23:03 pts/1    00:00:00 bash tpl.sh --unique-id tpl.sh_2
root     22697 22675  0 23:03 pts/1    00:00:00 bash tpl.sh --unique-id tpl.sh_3
# tail -f out*.log
==> out_1.log <==
2023-04-10 23:03:32

==> out_2.log <==
2023-04-10 23:03:32

==> out_3.log <==
2023-04-10 23:03:32

交给专业工具

专业的工具做专业的事情,比如 supervisor
root@VM-ubuntu:/etc/supervisor/conf.d# cat tpl.conf 
[program:tpl]
process_name=%(program_name)s_%(process_num)02d
command=bash /opt/tmp/tpl.sh 
stderr_logfile=/opt/tmp/%(program_name)s_%(process_num)02d_err.log
stdout_logfile=/opt/tmp/%(program_name)s_%(process_num)02d_out.log
autostart=true
user=root
autorestart=true
startretries=100
startsecs=10
numprocs=3
root@VM-ubuntu:/etc/supervisor/conf.d# supervisorctl 
supervisor> update
tpl: added process group
supervisor> status
tpl:tpl_00                       RUNNING   pid 27674, uptime 0:00:11
tpl:tpl_01                       RUNNING   pid 27671, uptime 0:00:11
tpl:tpl_02                       RUNNING   pid 27675, uptime 0:00:11
root@VM-ubuntu:/etc/supervisor/conf.d# pgrep -f tpl.sh
27671
27674
27675
root@VM-ubuntu:/etc/supervisor/conf.d# ps -ef | grep tpl.sh | grep -v "grep"
root     27671  1410  0 23:14 ?        00:00:00 bash /opt/tmp/tpl.sh
root     27674  1410  0 23:14 ?        00:00:00 bash /opt/tmp/tpl.sh
root     27675  1410  0 23:14 ?        00:00:00 bash /opt/tmp/tpl.sh

# tail -f tpl_0*
==> tpl_00_err.log <==

==> tpl_00_out.log <==
2023-04-10 23:15:24
2023-04-10 23:15:34
2023-04-10 23:15:44
2023-04-10 23:15:54

==> tpl_01_err.log <==

==> tpl_01_out.log <==
2023-04-10 23:15:24
2023-04-10 23:15:34
2023-04-10 23:15:44
2023-04-10 23:15:54

==> tpl_02_err.log <==

==> tpl_02_out.log <==
2023-04-10 23:15:24
2023-04-10 23:15:34
2023-04-10 23:15:44
2023-04-10 23:15:54