Jalankan skrip php sebagai proses daemon

154

Saya perlu menjalankan skrip php sebagai proses daemon (menunggu instruksi dan melakukan hal-hal). pekerjaan cron tidak akan melakukannya untuk saya karena tindakan harus diambil segera setelah instruksi tiba. Saya tahu PHP sebenarnya bukan pilihan terbaik untuk proses daemon karena masalah manajemen memori, tetapi karena berbagai alasan saya harus menggunakan PHP dalam kasus ini. Saya menemukan alat oleh libslack bernama Daemon ( http://libslack.org/daemon ) sepertinya membantu saya mengelola proses daemon, tetapi belum ada pembaruan dalam 5 tahun terakhir, jadi saya ingin tahu apakah Anda tahu beberapa alternatif lain yang cocok untuk kasus saya. Setiap informasi akan sangat dihargai.

Beier
sumber
2
tolong periksa jawaban saya. terima kasih
Henrik P. Hessel
1
Saya menemukan posting ini gonzalo123.com/2010/05/23/... yang saya percaya adalah santai dan stabil.
Teson
Ini sangat mudah dilakukan dengan systemd
LeonanCarvalho

Jawaban:

167

Anda bisa memulai skrip php dari baris perintah (yaitu bash) dengan menggunakan

nohup php myscript.php &

itu & menempatkan proses Anda di latar belakang.

Sunting:
Ya, ada beberapa kekurangan, tetapi tidak mungkin dikendalikan? Itu salah.
Sederhana kill processidakan menghentikannya. Dan itu masih solusi terbaik dan paling sederhana.

Henrik P. Hessel
sumber
Jika terminal ada, proses TIDAK akan keluar. Itu sebabnya perintah "nohup" ada di sana. Saya telah menggunakan skrip PHP sebagai daemon di semua server seperti ini selama bertahun-tahun sekarang. Mungkin ada solusi yang lebih baik, tetapi ini adalah yang tercepat.
CDR
27
Ini tidak akan memulai ulang daemon jika gagal, dan tidak ada cara mudah untuk mengelola daemon sama sekali.
Phil Wallach
6
Saya setuju dengan apa yang dikatakan di sini - ini adalah solusi yang buruk. Anda harus membuat skrip init karena beberapa alasan: 1) Skrip init diluncurkan secara otomatis saat startup 2) Anda dapat mengelola daemon dengan perintah start / stop / restart. Berikut adalah contoh dari servefault: serverfault.com/questions/229759/…
Simian
1
hei teman-teman ... menurut saya nohupdan &melakukan hal yang sama: melepaskan proses yang diluncurkan dari maksud shell saat ini. Mengapa saya membutuhkan keduanya? Bisakah saya tidak hanya melakukan php myscript.php &atau nohup myscript.php?? Terima kasih
nourdine
1
Jika skrip menulis ke stdout (via echo atau var_dump) maka Anda dapat menangkap informasi ini dengan file log seperti ini:nohup php myscript.php > myscript.log &
Mischa
167

Pilihan lain adalah menggunakan Pemula . Ini awalnya dikembangkan untuk Ubuntu (dan dikemas dengan itu secara default), tetapi dimaksudkan untuk cocok untuk semua distro Linux.

Pendekatan ini mirip dengan Supervisord dan daemontools , dalam hal ini secara otomatis memulai daemon pada boot sistem dan respawn pada penyelesaian skrip.

Cara mengaturnya:

Buat file skrip baru di /etc/init/myphpworker.conf. Berikut ini sebuah contoh:

# Info
description "My PHP Worker"
author      "Jonathan"

# Events
start on startup
stop on shutdown

# Automatically respawn
respawn
respawn limit 20 5

# Run the script!
# Note, in this example, if your PHP script returns
# the string "ERROR", the daemon will stop itself.
script
    [ $(exec /usr/bin/php -f /path/to/your/script.php) = 'ERROR' ] && ( stop; exit 1; )
end script

Mulai & hentikan dasmon Anda:

sudo service myphpworker start
sudo service myphpworker stop

Periksa apakah daemon Anda sedang berjalan:

sudo service myphpworker status

Terima kasih

Terima kasih banyak kepada Kevin van Zonneveld , tempat saya belajar teknik ini.

Jonathan
sumber
2
mencintai ini. Hanya ingin tahu, apakah mungkin untuk memiliki beberapa pekerja bersamaan? Saya hanya memiliki masalah bahwa satu pekerja tidak lagi cukup.
Manuel
1
apakah ini akan berjalan secara otomatis pada startup sistem?
slier
2
Sudo "service myphpworker start" tidak bekerja untuk saya. Saya menggunakan "sudo start myphpworker" dan bekerja dengan baik
Matt Sich
3
@Peptepta Itu karena ada kesalahan dalam posting - Saya tidak yakin apa (dan belum menguji ini), tapi saya pikir itu sudo service myphpworker start/stop/statushanya bekerja dengan layanan yang tidak dalam /etc/init.dlayanan pemula. @ matt-sich tampaknya telah mengungkap sintaks yang benar. Pilihan lain adalah menggunakan Gearman atau Resque, yang memungkinkan pemrosesan latar belakang & deamonisasi.
ckm
3
Ubuntu sendiri pindah ke menggunakan systemd bukan kaya baru: zdnet.com/article/after-linux-civil-war-ubuntu-to-adopt-systemd
Kzqai
72

Dengan systemd baru Anda dapat membuat layanan.

Anda harus membuat file atau symlink di /etc/systemd/system/, misalnya. myphpdaemon.service dan tempatkan konten seperti ini, myphpdaemon akan menjadi nama layanan:

[Unit]
Description=My PHP Daemon Service
#May your script needs MySQL or other services to run, eg. MySQL Memcached
Requires=mysqld.service memcached.service 
After=mysqld.service memcached.service

[Service]
User=root
Type=simple
TimeoutSec=0
PIDFile=/var/run/myphpdaemon.pid
ExecStart=/usr/bin/php -f /srv/www/myphpdaemon.php arg1 arg2> /dev/null 2>/dev/null
#ExecStop=/bin/kill -HUP $MAINPID #It's the default you can change whats happens on stop command
#ExecReload=/bin/kill -HUP $MAINPID
KillMode=process

Restart=on-failure
RestartSec=42s

StandardOutput=null #If you don't want to make toms of logs you can set it null if you sent a file or some other options it will send all php output to this one.
StandardError=/var/log/myphpdaemon.log
[Install]
WantedBy=default.target

Anda akan dapat memulai, mendapatkan status, memulai kembali, dan menghentikan layanan menggunakan perintah

systemctl <start|status|restart|stop|enable> myphpdaemon

Skrip PHP harus memiliki semacam "lingkaran" untuk terus berjalan.

<?php
gc_enable();//
while (!connection_aborted() || PHP_SAPI == "cli") {

  //Code Logic

  //sleep and usleep could be useful
    if (PHP_SAPI == "cli") {
        if (rand(5, 100) % 5 == 0) {
            gc_collect_cycles(); //Forces collection of any existing garbage cycles
        }
    }
}

Contoh kerja:

[Unit]
Description=PHP APP Sync Service
Requires=mysqld.service memcached.service
After=mysqld.service memcached.service

[Service]
User=root
Type=simple
TimeoutSec=0
PIDFile=/var/run/php_app_sync.pid
ExecStart=/bin/sh -c '/usr/bin/php -f /var/www/app/private/server/cron/app_sync.php  2>&1 > /var/log/app_sync.log'
KillMode=mixed

Restart=on-failure
RestartSec=42s

[Install]
WantedBy=default.target

Jika rutin PHP Anda harus dijalankan sekali dalam satu siklus (seperti diggest), Anda dapat menggunakan shell atau skrip bash untuk dipanggil ke file layanan systemd alih-alih PHP secara langsung, misalnya:

#!/usr/bin/env bash
script_path="/app/services/"

while [ : ]
do
#    clear
    php -f "$script_path"${1}".php" fixedparameter ${2}  > /dev/null 2>/dev/null
    sleep 1
done

Jika Anda memilih opsi ini, Anda harus mengubah KillMode ke mixedke proses, bash (utama) dan PHP (anak) terbunuh.

ExecStart=/app/phpservice/runner.sh phpfile parameter  > /dev/null 2>/dev/null
KillMode=process

This method also is effective if you're facing a memory leak.

Catatan: Setiap kali Anda mengubah "myphpdaemon.service" Anda, Anda harus menjalankan `systemctl daemon-reload ', tetapi jangan khawatir jika tidak melakukannya, itu akan diperingatkan ketika dibutuhkan.

LeonanCarvalho
sumber
7
Jawaban yang diremehkan. Anda memiliki +1 saya.
Gergely Lukacsy
2
Luar biasa. Seandainya kami juga bisa menjawab dengan hati-hati karena ini tidak boleh dikubur di halaman ini.
Justin
1
Anda harus memeriksa systemctl status <your_service_name> -loutput, itu akan memberi Anda petunjuk apa yang terjadi.
LeonanCarvalho
1
@LeandroTupone MySQL dan Memcached adalah demonstrasi cara menggunakan dependensi layanan, itu tidak perlu.
LeonanCarvalho
3
Ini benar-benar harus menggantikan jawaban yang diterima karena sekarang 2019.
spice
47

Jika Anda dapat - ambil salinan Pemrograman Tingkat Lanjut di Lingkungan UNIX . Seluruh bab 13 dikhususkan untuk pemrograman daemon. Contohnya adalah di C, tapi semua fungsi yang Anda perlu memiliki pembungkus di PHP (pada dasarnya pcntl dan POSIX ekstensi).

Dalam beberapa kata - menulis daemon (ini hanya dimungkinkan pada * nix OS-es berbasis - Windows menggunakan layanan) adalah seperti ini:

  1. Panggilan umask(0)untuk mencegah masalah izin.
  2. fork() dan minta orang tua keluar.
  3. Panggilan setsid() .
  4. Pengaturan pemrosesan sinyal SIGHUP(biasanya diabaikan atau digunakan untuk memberi sinyal daemon untuk memuat ulang konfigurasinya) danSIGTERM (untuk memberi tahu proses untuk keluar dengan anggun).
  5. fork() lagi dan minta orang tua keluar.
  6. Ubah direktori kerja saat ini dengan chdir().
  7. fclose() stdin, stdoutdan stderrdan jangan menulis kepada mereka. Cara yang benar adalah dengan mengarahkan mereka ke salah satunya/dev/null atau file, tetapi saya tidak dapat menemukan cara untuk melakukannya dalam PHP. Mungkin saja ketika Anda meluncurkan daemon untuk mengarahkan mereka menggunakan shell (Anda harus mencari tahu sendiri bagaimana melakukan itu, saya tidak tahu :).
  8. Lakukan pekerjaanmu!

Juga, karena Anda menggunakan PHP, berhati-hatilah untuk referensi siklik, karena pemulung sampah PHP, sebelum PHP 5.3, tidak memiliki cara untuk mengumpulkan referensi tersebut dan proses kehabisan memori akan bocor, sampai akhirnya crash.

Emil Ivanov
sumber
1
Terimakasih atas infonya. Sepertinya program daemon libslack cukup banyak melakukan semua pekerjaan persiapan seperti yang Anda sebutkan. Saya pikir untuk saat ini saya akan tetap menggunakannya sampai saya menemukan alternatif lain yang baik.
Beier
1
Menemukan posting ini, kode yang diharapkan untuk menyalin & menempel ke aplikasi tua jelek yang gagal menutup stdin dll, kecewa. : p
ThiefMaster
1
Kenapa (5) bercabang () lagi?
TheFox
Bekerja untuk saya - pekerjaan bagus!
Gautam Sharma
Untuk pembaca yang akan datang bertanya mengapa harus bercabang dua kali: stackoverflow.com/questions/881388/… - TL; DR: Mencegah zombie.
Ghedipunk
24

Saya menjalankan sejumlah besar daemon PHP.

Saya setuju dengan Anda bahwa PHP bukan bahasa yang terbaik (atau bahkan bahasa yang baik) untuk melakukan ini, tetapi daemon berbagi kode dengan komponen yang menghadap web sehingga secara keseluruhan itu adalah solusi yang baik bagi kami.

Kami menggunakan daemontools untuk ini. Ini cerdas, bersih dan dapat diandalkan. Sebenarnya kita menggunakannya untuk menjalankan semua daemon kita.

Anda dapat melihatnya di http://cr.yp.to/daemontools.html .

EDIT: Daftar fitur yang cepat.

  • Secara otomatis memulai daemon saat reboot
  • Secara otomatis restart dameon pada kegagalan
  • Penebangan ditangani untuk Anda, termasuk rollover dan pemangkasan
  • Antarmuka manajemen: 'svc' dan 'svstat'
  • Ramah UNIX (mungkin bukan nilai tambah untuk semua orang)
Phil Wallach
sumber
Juga dapat diinstal dari repositori, misalnya di apt!
Kzqai
14

Kamu bisa

  1. Gunakan nohupseperti yang disarankan Henrik.
  2. Gunakan screendan jalankan program PHP Anda sebagai proses reguler di dalamnya. Ini memberi Anda lebih banyak kontrol daripada menggunakan nohup.
  3. Gunakan daemoniser seperti http://supervisord.org/ (ditulis dengan Python tetapi dapat daemonise program baris perintah apa pun dan memberi Anda kendali jarak jauh untuk mengelolanya).
  4. Tulis pembungkus daemonise Anda sendiri seperti yang disarankan Emil tetapi IMO itu berlebihan.

Saya akan merekomendasikan metode paling sederhana (layar menurut saya) dan kemudian jika Anda ingin lebih banyak fitur atau fungsi, pindah ke metode yang lebih kompleks.

Noufal Ibrahim
sumber
Bisakah Anda memberikan konfigurasi supervisor yang serupa?
Alix Axel
11

Ada lebih dari satu cara untuk menyelesaikan masalah ini.

Saya tidak tahu secara spesifik, tetapi mungkin ada cara lain untuk memicu proses PHP. Misalnya jika Anda memerlukan kode untuk menjalankan berdasarkan peristiwa dalam database SQL Anda bisa mengatur pemicu untuk mengeksekusi skrip Anda. Ini sangat mudah dilakukan di bawah PostgreSQL: http://www.postgresql.org/docs/current/static/external-pl.html .

Jujur saya pikir taruhan terbaik Anda adalah membuat proses Damon menggunakan nohup. nohup memungkinkan perintah untuk terus mengeksekusi bahkan setelah pengguna keluar:

nohup php myscript.php &

Namun ada masalah yang sangat serius. Seperti yang Anda katakan, manajer memori PHP adalah sampah lengkap, itu dibangun dengan asumsi bahwa skrip hanya dijalankan selama beberapa detik dan kemudian ada. Skrip PHP Anda akan mulai menggunakan GIGABYTES memori setelah hanya beberapa hari. Anda HARUS JUGA membuat skrip cron yang berjalan setiap 12 atau mungkin 24 jam yang membunuh dan memunculkan kembali skrip php Anda seperti ini:

killall -3 php
nohup php myscript.php &

Tapi bagaimana jika naskahnya ada di tengah pekerjaan? Well kill -3 adalah interupsi, sama seperti melakukan ctrl + c pada CLI. Script php Anda dapat menangkap interupsi ini dan keluar dengan anggun menggunakan pustaka pcntl PHP: http://php.oregonstate.edu/manual/en/function.pcntl-signal.php

Berikut ini sebuah contoh:

function clean_up() {
  GLOBAL $lock;
  mysql_close();
  fclose($lock)
  exit();
}
pcntl_signal(SIGINT, 'clean_up');

Gagasan di balik $ lock adalah bahwa skrip PHP dapat membuka file dengan fopen ("file", "w") ;. Hanya satu proses yang dapat memiliki kunci tulis pada file, jadi dengan menggunakan ini Anda dapat memastikan bahwa hanya satu salinan skrip PHP Anda yang sedang berjalan.

Semoga berhasil!

benteng
sumber
6

Lihat https://github.com/shaneharter/PHP-Daemon

Ini adalah perpustakaan daemon berorientasi objek. Ini memiliki dukungan bawaan untuk hal-hal seperti logging dan pemulihan kesalahan, dan memiliki dukungan untuk menciptakan pekerja latar belakang.

Shane H
sumber
3

Baru-baru ini saya membutuhkan solusi lintas platform (Windows, Mac, dan Linux) untuk masalah menjalankan skrip PHP sebagai daemon. Saya memecahkan masalah dengan menulis solusi berbasis C ++ saya sendiri dan membuat binari:

https://github.com/cubiclesoft/service-manager/

Dukungan penuh untuk Linux (via sysvinit), tetapi juga layanan Windows NT dan Mac OSX launchd.

Jika Anda hanya membutuhkan Linux, maka beberapa solusi lain yang disajikan di sini berfungsi dengan baik dan, tergantung pada rasanya. Ada juga pemula dan systemd hari ini, yang memiliki cadangan untuk skrip sysvinit. Tetapi setengah dari titik menggunakan PHP adalah bahwa itu adalah lintas platform, sehingga kode yang ditulis dalam bahasa tersebut memiliki peluang yang cukup bagus untuk bekerja di mana saja sebagaimana adanya. Kekurangan mulai muncul ketika aspek tingkat OS asli eksternal tertentu memasuki gambar seperti layanan sistem, tetapi Anda akan mendapatkan masalah itu dengan sebagian besar bahasa skrip.

Mencoba menangkap sinyal seperti yang disarankan seseorang di sini di PHP userland bukanlah ide yang bagus. Baca dokumentasi dengan pcntl_signal()cermat dan Anda akan segera belajar bahwa PHP menangani sinyal menggunakan beberapa metode yang agak tidak menyenangkan (khususnya, 'kutu') yang mengunyah banyak siklus untuk sesuatu yang jarang dilihat oleh proses (yaitu sinyal). Penanganan sinyal dalam PHP juga hampir tidak tersedia pada platform POSIX dan dukungan berbeda berdasarkan versi PHP. Awalnya terdengar seperti solusi yang layak tetapi gagal menjadi sangat berguna.

PHP juga menjadi lebih baik tentang masalah kebocoran memori seiring berjalannya waktu. Anda masih harus berhati-hati (parser DOM XML cenderung masih bocor) tapi saya jarang melihat proses pelarian hari ini dan pelacak bug PHP cukup tenang dibandingkan dengan zaman dahulu kala.

CubicleSoft
sumber
1

Seperti yang telah disebutkan orang lain, menjalankan PHP sebagai daemon cukup mudah, dan dapat dilakukan dengan menggunakan satu baris perintah. Tetapi masalah sebenarnya adalah membuatnya tetap berjalan dan mengelolanya. Saya memiliki masalah yang sama beberapa waktu lalu dan meskipun ada banyak solusi yang sudah tersedia, kebanyakan dari mereka memiliki banyak dependensi atau sulit digunakan dan tidak cocok untuk penggunaan dasar. Saya menulis skrip shell yang dapat mengelola proses / aplikasi apa pun termasuk skrip PHP cli. Itu dapat diatur sebagai cronjob untuk memulai aplikasi dan akan berisi aplikasi dan mengelolanya. Jika dijalankan lagi, misalnya melalui cronjob yang sama, itu memeriksa apakah aplikasi sedang berjalan atau tidak, jika itu keluar maka cukup keluar dan biarkan instance sebelumnya terus mengelola aplikasi.

Saya mengunggahnya ke github, silakan menggunakannya: https://github.com/sinasalek/EasyDeamonizer

EasyDeamonizer

Cukup mengawasi aplikasi Anda (mulai, mulai ulang, masuk, monitor, dll). skrip generik untuk memastikan aplikasi Anda tetap berjalan dengan baik. Secara sengaja ia menggunakan proses nama instread dari file pid / lock untuk mencegah semua efek sampingnya dan menjaga skrip sesederhana dan sekuat mungkin, sehingga selalu berfungsi bahkan ketika EasyDaemonizer itu sendiri dihidupkan ulang. fitur

  • Mulai aplikasi dan secara opsional penundaan yang disesuaikan untuk setiap awal
  • Pastikan hanya satu instance yang berjalan
  • Monitor penggunaan CPU dan restart aplikasi secara otomatis ketika mencapai ambang yang ditentukan
  • Mengatur EasyDeamonizer untuk dijalankan melalui cron untuk menjalankannya lagi jika dihentikan karena alasan apa pun
  • Catat aktivitasnya
Sina Salek
sumber
1

Memperluas jawaban Emil Ivaov , Anda dapat melakukan yang berikut untuk menutup STDIN, STDOUT DAN STDERROR di php

if (!fclose(STDIN)) {
    exit("Could not close STDIN");
}

if (!fclose(STDOUT)) {
    exit("Could not close STDOUT");
}

if (!fclose(STDERR)) {
    exit("Could not close STDERR");
}

$STDIN = fopen('/dev/null', 'r');
$STDOUT = fopen('/dev/null', 'w');
$STDERR = fopen('/var/log/our_error.log', 'wb');

Pada dasarnya Anda menutup stream standar sehingga PHP tidak memiliki tempat untuk menulis. fopenPanggilan berikut akan mengatur standar IO ke /dev/null.

Saya telah membaca ini dari buku Rob Aley - PHP di luar web

Raheel
sumber
0

Anda dapat memeriksa pm2 di sini adalah, http://pm2.keymetrics.io/

buat file ssh, seperti pekerja.sh dimasukkan ke dalam skrip php Anda yang akan Anda tangani.

pekerja

php /path/myscript.php

daemon mulai

pm2 start worker.sh

Ceria, itu dia.

Serkan Koch
sumber