วิธีการแก้ปัญหาทั่วไปเพื่อป้องกันไม่ให้งาน cron ยาวทำงานในแบบคู่ขนาน?


27

ฉันกำลังมองหาโซลูชันที่เรียบง่ายและทั่วไปที่จะช่วยให้คุณสามารถรันสคริปต์หรือแอปพลิเคชันใด ๆ ใน crontab และป้องกันไม่ให้ทำงานสองครั้ง

การแก้ปัญหาควรเป็นอิสระในคำสั่งดำเนินการ

ฉันคิดว่ามันควรจะดูเหมือนว่าlock && (command ; unlock)ที่ล็อคจะกลับเท็จถ้ามีล็อคอื่น

ส่วนที่สองจะเป็นเช่นนั้นหากได้รับการล็อกให้เรียกใช้คำสั่งและปลดล็อกหลังจากดำเนินการคำสั่งแม้ว่าจะส่งกลับข้อผิดพลาด

คำตอบ:


33

ดูแพคเกจrun-one ติดตั้ง run-oneจากmanpage สำหรับrun-oneคำสั่งไอคอน Manpage :

run-oneเป็นสคริปต์ wrapper ที่รันอินสแตนซ์ที่ไม่ซ้ำกันของคำสั่งบางคำสั่งพร้อมชุดอาร์กิวเมนต์ที่ไม่ซ้ำกัน

สิ่งนี้มักจะมีประโยชน์สำหรับ cronjobs เมื่อคุณต้องการให้มีมากกว่าหนึ่งชุดในเวลาเดียวกัน

เช่นเดียวกับtimeหรือsudoคุณเพิ่งเติมมันลงในคำสั่ง ดังนั้น cronjob อาจมีลักษณะ:

  */60 * * * *   run-one rsync -azP $HOME example.com:/srv/backup

สำหรับข้อมูลเพิ่มเติมและพื้นหลังตรวจสอบโพสต์บล็อกแนะนำโดยดัสตินเคิร์กแลนด์


5

วิธีง่ายๆในการตั้งล็อค:

if mkdir /var/lock/mylock; then
  echo "Locking succeeded" >&2
else
  echo "Lock failed - exit" >&2
  exit 1
fi

สคริปต์ที่ต้องการเรียกใช้ต้องสร้างล็อค หากล็อคอยู่สคริปต์อื่นจะไม่ว่างดังนั้นสคริปต์แรกจึงไม่สามารถทำงานได้ หากไฟล์ไม่มีอยู่สคริปต์จะไม่ได้รับการล็อค ดังนั้นสคริปต์ปัจจุบันจึงได้รับการล็อค เมื่อสคริปต์เสร็จสิ้นการล็อคจะต้องมีการ realeased โดยการลบล็อค

สำหรับข้อมูลเพิ่มเติมเกี่ยวกับการล็อคทุบตีตรวจสอบหน้านี้


1
คุณจะต้องการแทร็ก EXIT ที่ปลดล็อคเมื่อออก echo "Locking succeeded" >&2; trap 'rm -rf /var/lock/mylock' EXIT
geirha

1
เป็นการดีที่คุณต้องการใช้ฝูงที่ปรึกษาจากกระบวนการที่เรียกใช้คำสั่งที่คุณต้องการเป็นงานย่อย วิธีที่ว่าถ้าพวกเขาตายทั้งหมดฝูงถูกปล่อยออกมาโดยอัตโนมัติซึ่งใช้การปรากฏตัวของไฟล์ล็อคไม่ได้ทำ ใช้พอร์ตเครือข่ายจะทำงานในลักษณะที่คล้ายกัน - แต่นั่นเป็นวิธีที่ namespace ขนาดเล็กซึ่งเป็นปัญหา
Alex North-Keys

3

ไม่จำเป็นต้องติดตั้งแพ็คเกจแฟนซี:

#!/bin/bash
pgrep -xf "$*" > /dev/null || "$@"

มันเร็วกว่าที่จะเขียนสคริปต์นั้นด้วยตัวคุณเองมากกว่าเรียกใช้ "apt-get install" ใช่ไหม? คุณอาจต้องการเพิ่ม "-u $ (id -u)" ใน pgrep เพื่อตรวจสอบอินสแตนซ์ที่ดำเนินการโดยผู้ใช้ปัจจุบันเท่านั้น


2
สิ่งนี้ไม่ได้รับประกันอินสแตนซ์เดียว สคริปต์สองตัวสามารถส่งผ่านไปยังอีกฝั่งหนึ่งของ||โอเปอเรเตอร์ในเวลาเดียวกันก่อนที่จะมีโอกาสเริ่มต้นสคริปต์
Sedat Kapanoglu

@SedatKapanoglu ได้รับสคริปต์ดังกล่าวไม่ได้เป็นหลักฐานการแข่งขัน แต่คำถามเดิมเกี่ยวกับ cron-jobs ที่ใช้เวลานาน หากระบบของคุณต้องการเวลามากกว่าหนึ่งนาทีในการสร้างกระบวนการที่คุณมีปัญหาอื่น ๆ อย่างไรก็ตามหากจำเป็นด้วยเหตุผลอื่นคุณสามารถใช้ฝูง (1) เพื่อป้องกันสคริปต์ข้างต้นจากสภาพการแข่งขัน
Michael Kowhan

ฉันใช้สิ่งนี้ แต่สำหรับสคริปต์ทุบตีที่ควรตรวจสอบตัวเอง รหัสคือ: v = $ (pgrep -xf "/ bin / bash $ 0 $ @") ["$ {v / $ BASHPID /}"! = ""] && ออกจาก 2
ahofmann

3

ดูเพิ่มเติมที่ Tim Kay's soloซึ่งดำเนินการล็อคโดยผูกพอร์ตในที่อยู่ย้อนกลับที่ไม่ซ้ำกับผู้ใช้:

http://timkay.com/solo/

ในกรณีที่ไซต์ของเขาหยุดทำงาน:

การใช้งาน:

solo -port=PORT COMMAND

where
    PORT        some arbitrary port number to be used for locking
    COMMAND     shell command to run

options
    -verbose    be verbose
    -silent     be silent

ใช้แบบนี้:

* * * * * solo -port=3801 ./job.pl blah blah

สคริปต์:

#!/usr/bin/perl -s
#
# solo v1.7
# Prevents multiple cron instances from running simultaneously.
#
# Copyright 2007-2016 Timothy Kay
# http://timkay.com/solo/
#
# It is free software; you can redistribute it and/or modify it under the terms of either:
#
# a) the GNU General Public License as published by the Free Software Foundation;
#    either version 1 (http://dev.perl.org/licenses/gpl1.html), or (at your option)
#    any later version (http://www.fsf.org/licenses/licenses.html#GNUGPL), or
#
# b) the "Artistic License" (http://dev.perl.org/licenses/artistic.html), or
#
# c) the MIT License (http://opensource.org/licenses/MIT)
#

use Socket;

alarm $timeout                              if $timeout;

$port =~ /^\d+$/ or $noport                     or die "Usage: $0 -port=PORT COMMAND\n";

if ($port)
{
    # To work with OpenBSD: change to
    # $addr = pack(CnC, 127, 0, 1);
    # but make sure to use different ports across different users.
    # (Thanks to  www.gotati.com .)
    $addr = pack(CnC, 127, $<, 1);
    print "solo: bind ", join(".", unpack(C4, $addr)), ":$port\n"   if $verbose;

    $^F = 10;           # unset close-on-exec

    socket(SOLO, PF_INET, SOCK_STREAM, getprotobyname('tcp'))       or die "socket: $!";
    bind(SOLO, sockaddr_in($port, $addr))               or $silent? exit: die "solo($port): $!\n";
}

sleep $sleep if $sleep;

exec @ARGV;

สำหรับ Mac OSX นี้จะล้มเหลวด้วยข้อผิดพลาดsolo(3801): Can't assign requested addressเว้นแต่คุณจะบังคับ0สำหรับพารามิเตอร์ที่ 3 packของวิธีการ สิ่งที่ดีสำหรับ BSD ก็ดีสำหรับ Mac เช่นกัน
Eric Leschinski

1

คุณต้องล็อค run-oneทำงานได้ แต่คุณอาจต้องการดูflockจากutil-linuxแพ็คเกจ

มันเป็นแพ็คเกจมาตรฐานที่นักพัฒนาเคอร์เนลให้มาซึ่งสามารถปรับแต่งได้มากกว่าrun-oneและยังง่ายมาก


0

วิธีง่ายๆจากbash-hackers.orgที่ทำงานสำหรับฉันคือการใช้mkdir นี่เป็นวิธีง่ายๆในการตรวจสอบให้แน่ใจว่ามีโปรแกรมของคุณทำงานเพียงหนึ่งอินสแตนซ์ สร้างไดเรกทอรีด้วยmkdir .lockซึ่งจะส่งกลับ

  • จริงถ้าการสร้างประสบความสำเร็จและ
  • falseหากไฟล์ล็อกมีอยู่ซึ่งบ่งชี้ว่าขณะนี้มีหนึ่งอินสแตนซ์กำลังทำงานอยู่

ดังนั้นฟังก์ชั่นที่เรียบง่ายนี้ทำตรรกะล็อกไฟล์ทั้งหมด:

if mkdir .lock; then
    echo "Locking succeeded"
    eval startYourProgram.sh ;
else
    echo "Lock file exists. Program already running? Exit. "
    exit 1
fi


echo "Program finished, Removing lock."
rm -r .lock

0

วิธีนี้มีไว้สำหรับสคริปต์ทุบตีที่ต้องตรวจสอบตัวเอง

v=$(pgrep -xf "/bin/bash $0 $@")
[ "${v/$BASHPID/}" != "" ] && exit 0
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.