Pertanyaan Mencegah pekerjaan cron duplikat berjalan


Saya telah menjadwalkan tugas cron untuk dijalankan setiap menit, tetapi terkadang skrip membutuhkan lebih dari satu menit untuk selesai dan saya tidak ingin pekerjaan mulai "menumpuk" satu sama lain. Saya kira ini adalah masalah konkurensi - yaitu eksekusi skrip harus saling eksklusif.

Untuk mengatasi masalah saya membuat skrip mencari keberadaan file tertentu ("lockfile.txt") dan keluar jika ada atau touch jika tidak. Tapi ini semaphore yang cukup buruk! Apakah ada praktik terbaik yang harus saya ketahui? Haruskah saya menulis sebuah daemon?


81
2017-11-09 11:32






Jawaban:


Ada beberapa program yang mengotomatiskan fitur ini, menghilangkan gangguan dan potensi bug dari melakukan ini sendiri, dan menghindari masalah kunci basi dengan menggunakan kawanan di belakang layar juga (yang merupakan risiko jika Anda hanya menggunakan sentuhan) . Saya telah menggunakan lockrun dan lckdo di masa lalu, tetapi sekarang ada flock(1) (dalam versi yang lebih baru dari util-linux) yang sangat bagus. Ini sangat mudah digunakan:

* * * * * /usr/bin/flock -n /tmp/fcj.lockfile /usr/local/bin/frequent_cron_job

109
2017-11-09 11:57



lckdo akan dihapus dari leburan, sekarang kawanan (1) ada di util-linux. Dan paket itu pada dasarnya adalah wajib dalam sistem Linux, jadi Anda harus dapat mengandalkan kehadirannya. Untuk penggunaan, lihat di bawah. - jldugger
Ya, kawanan sekarang pilihan saya disukai. Saya bahkan akan memperbarui jawaban saya sesuai. - womble♦
Apakah ada yang tahu perbedaannya flock -n file command dan flock -n file -c command ? - Nanne
@Nanne, saya harus memeriksa kode untuk memastikan, tapi tebakan saya adalah itu -c menjalankan perintah yang ditentukan melalui shell (sesuai dengan manpage), sedangkan "kosong" (non--c) terbentuk saja execs perintah yang diberikan. Menempatkan sesuatu melalui shell memungkinkan Anda melakukan hal-hal seperti shell (seperti menjalankan beberapa perintah yang dipisahkan dengan ; atau &&), tetapi juga membuka Anda untuk melakukan serangan perluasan shell jika Anda menggunakan input yang tidak tepercaya. - womble♦
Itu adalah argumen untuk (hipotetis) frequent_cron_job perintah yang mencoba untuk menunjukkan itu sedang dijalankan setiap menit. Saya telah menghapusnya karena tidak ada yang bermanfaat, dan menyebabkan kebingungan (milik Anda, jika tidak ada orang lain selama bertahun-tahun). - womble♦


Cara terbaik dalam shell adalah menggunakan kawanan domba (1)

(
  flock -x -w 5 99
  ## Do your stuff here
) 99>/path/to/my.lock

27
2017-11-09 11:45



Saya tidak bisa tidak menyarankan penggunaan fd redirection yang rumit. Ini terlalu mengasyikkan. - womble♦
Tidak mem-parsing saya di Bash atau ZSH, perlu menghilangkan ruang antara 99 dan > begitulah 99> /... - Kyle Brandt♦
@Javier: Bukan berarti itu tidak rumit dan misterius, hanya itu saja didokumentasikan, rumit, dan misterius. - womble♦
apa yang akan terjadi jika Anda me-restart saat ini berjalan atau prosesnya terbunuh entah bagaimana? Apakah akan dikunci selamanya? - Alex R
Saya memahami struktur ini menciptakan kunci eksklusif tetapi saya tidak mengerti mekanisme bagaimana hal ini tercapai. Apa fungsi dari '99' dalam jawaban ini? Ada yang peduli untuk menjelaskan ini? Terima kasih! - Asciiom


Sebenarnya, flock -n dapat digunakan sebagai gantinya lckdo*, jadi Anda akan menggunakan kode dari pengembang kernel.

Membangun contoh womble, Anda akan menulis sesuatu seperti:

* * * * * flock -n /some/lockfile command_to_run_every_minute

BTW, lihat kode, semua flock, lockrun, dan lckdo lakukan hal yang sama persis, jadi hanya masalah yang paling siap tersedia untuk Anda.

* Karena, mengingat reputasi saya pada saat menulis, saya tidak dapat mengedit atau mengomentari jawaban sebelumnya, saya harus menulis ini sebagai jawaban yang terpisah.


21
2017-11-19 22:43





Anda dapat menggunakan file kunci. Buat file ini ketika skrip dimulai dan hapus ketika selesai. Skrip, sebelum menjalankan rutinitas utamanya, harus memeriksa apakah file kunci ada dan diproses sesuai dengan itu.

Lockfiles digunakan oleh skrip init dan oleh banyak aplikasi dan utilitas lain dalam sistem Unix.


2
2017-11-09 11:36



ini adalah hanya cara yang pernah saya lihat itu dilaksanakan, secara pribadi. Saya menggunakan sesuai saran pengelola sebagai cermin untuk proyek OSS - warren


Ini mungkin juga pertanda bahwa Anda melakukan hal yang salah. Jika pekerjaan Anda berjalan sangat dekat dan sering, mungkin Anda harus mempertimbangkan mendeskripsikannya dan menjadikannya program bergaya daemon.


1
2017-11-09 11:45



Saya sungguh tidak setuju dengan ini. Jika Anda memiliki sesuatu yang perlu dijalankan secara berkala, menjadikannya daemon adalah solusi "sledgehammer for a nut". Menggunakan lockfile untuk mencegah kecelakaan adalah solusi yang masuk akal yang tidak pernah saya hadapi. - womble♦
@womble Saya setuju; tapi aku suka menghancurkan kacang dengan palu godam! :-) - wzzrd


Anda belum ditentukan jika Anda ingin skrip menunggu proses sebelumnya selesai atau tidak. Dengan "Saya tidak ingin pekerjaan mulai" menumpuk "satu sama lain", saya rasa Anda menyiratkan bahwa Anda ingin skrip untuk keluar jika sudah berjalan,

Jadi, jika Anda tidak ingin bergantung pada lckdo atau serupa, Anda dapat melakukan ini:


PIDFILE=/tmp/`basename $0`.pid

if [ -f $PIDFILE ]; then
  if ps -p `cat $PIDFILE` > /dev/null 2>&1; then
      echo "$0 already running!"
      exit
  fi
fi
echo $$ > $PIDFILE

trap 'rm -f "$PIDFILE" >/dev/null 2>&1' EXIT HUP KILL INT QUIT TERM

# do the work


1
2017-11-09 14:33



Terima kasih contoh Anda sangat membantu - Saya ingin skrip untuk keluar jika sudah berjalan. Terima kasih sudah menyebutkan ickdo - tampaknya melakukan trik. - Tom


Daemon cron Anda tidak seharusnya menjalankan tugas jika instance sebelumnya masih berjalan. Saya adalah pengembang dari satu cron daemon dcron, dan kami secara khusus berusaha mencegahnya. Saya tidak tahu bagaimana Vixie cron atau daemon lain menangani ini.


1
2018-02-17 15:59





Saya akan merekomendasikan menggunakan run-one perintah - jauh lebih sederhana daripada menangani kunci. Dari dokumen:

run-one adalah skrip pembungkus yang berjalan tidak lebih dari satu contoh unik dari beberapa perintah dengan seperangkat argumen unik. Ini sering berguna dengan cronjobs, ketika Anda ingin tidak lebih dari satu salinan berjalan pada suatu waktu.

jalankan ini sama persis seperti run-one, kecuali bahwa ia akan menggunakan pgrep dan kill untuk menemukan dan mematikan proses yang berjalan yang dimiliki oleh pengguna dan mencocokkan perintah dan argumen target. Perhatikan bahwa run-this-one akan memblokir ketika mencoba untuk membunuh proses yang cocok, sampai semua yang cocok proses mati.

run-one-constant beroperasi persis seperti run-one kecuali itu respawns "COMMAND [ARGS]" kapan saja COMMAND keluar (nol atau tidak nol).

tetap berjalan satu arah adalah alias untuk run-one-constant.

jalankan satu sampai sukses beroperasi persis seperti run-one-constant kecuali bahwa itu respawns "COMMAND [ARGS]" sampai COMMAND berhasil keluar (yaitu, keluar dari nol).

run-one-hingga-kegagalan beroperasi persis seperti run-one-constant kecuali bahwa itu respawns "COMMAND [ARGS]" sampai COMMAND berakhir dengan kegagalan (yaitu, keluar bukan nol).


1
2017-10-02 21:53





Saya telah membuat satu toples untuk memecahkan masalah seperti duplikat duplikat yang berjalan bisa berupa java atau shell cron. Hanya lewat nama cron di Duplicates.CloseSessions ("Demo.jar") ini akan mencari dan membunuh existng pid untuk cron ini kecuali saat ini. Saya telah menerapkan metode untuk melakukan hal ini. String proname = ManagementFactory.getRuntimeMXBean (). GetName ();                 String pid = proname.split ("@") [0];                 System.out.println ("Current PID:" + pid);

            Process proc = Runtime.getRuntime().exec(new String[]{"bash","-c"," ps aux | grep "+cronname+" | awk '{print $2}' "});

            BufferedReader stdInput = new BufferedReader(new InputStreamReader(proc.getInputStream()));
            String s = null;
            String killid="";

            while ((s = stdInput.readLine()) != null ) {                                        
                if(s.equals(pid)==false)
                {
                    killid=killid+s+" ";    
                }
            }

Dan kemudian bunuh string killid dengan perintah shell lagi


0
2017-12-28 08:36



Saya tidak berpikir ini benar-benar menjawab pertanyaan itu. - kasperd