Saya mencoba melakukan panggilan AJAX (melalui JQuery) yang akan memulai proses yang cukup lama. Saya ingin skrip hanya mengirim respons yang menunjukkan bahwa proses telah dimulai, tetapi JQuery tidak akan mengembalikan respons sampai skrip PHP selesai dijalankan.
Saya sudah mencoba ini dengan header "tutup" (di bawah), dan juga dengan buffering keluaran; sepertinya tidak ada yang berhasil. Ada tebakan? atau apakah ini sesuatu yang perlu saya lakukan di JQuery?
<?php
echo( "We'll email you as soon as this is done." );
header( "Connection: Close" );
// do some stuff that will take a while
mail( '[email protected]', "okay I'm done", 'Yup, all done.' );
?>
Jawaban:
Halaman manual PHP berikut (termasuk catatan pengguna) menyarankan beberapa petunjuk tentang cara menutup koneksi TCP ke browser tanpa mengakhiri skrip PHP:
Seharusnya itu membutuhkan lebih dari sekedar mengirimkan header yang tertutup.
OP kemudian mengonfirmasi: ya, ini melakukan trik: menunjuk ke catatan pengguna # 71172 (Nov 2006) disalin di sini:
Kemudian pada bulan Juli 2010 dalam jawaban terkait Arctic Fire kemudian menghubungkan dua catatan pengguna lebih lanjut yang merupakan tindak lanjut dari yang di atas:
sumber
Connection: close
header dapat ditimpa oleh perangkat lunak lain di stack, misalnya, proxy terbalik dalam kasus CGI (saya mengamati perilaku itu dengan nginx). Lihat jawaban dari @hanshenrik tentang itu. Secara umum,Connection: close
dijalankan di sisi klien, dan tidak boleh dianggap sebagai jawaban atas pertanyaan ini. Koneksi harus ditutup dari sisi server .Anda perlu mengirim 2 header ini:
Karena Anda perlu mengetahui ukuran output Anda, Anda perlu buffer output Anda, lalu flush ke browser:
Selain itu, jika server web Anda menggunakan kompresi gzip otomatis pada keluaran (mis. Apache dengan mod_deflate), ini tidak akan berfungsi karena ukuran keluaran yang sebenarnya berubah, dan Panjang Konten tidak lagi akurat. Nonaktifkan kompresi gzip pada skrip tertentu.
Untuk detail lebih lanjut, kunjungi http://www.zulius.com/how-to/close-browser-connection-continue-execution
sumber
header("Content-Encoding: none\r\n");
cara itu apache tidak akan mengompresnya.ob_flush()
tidak perlu dan sebenarnya menyebabkan pemberitahuanfailed to flush buffer
. Saya mengeluarkannya dan ini bekerja dengan baik.ob_flush()
garis itu perlu.Anda dapat menggunakan Fast-CGI dengan PHP-FPM untuk menggunakan
fastcgi_end_request()
fungsi tersebut . Dengan cara ini, Anda dapat terus melakukan beberapa pemrosesan sementara respons telah dikirim ke klien.Anda menemukan ini di manual PHP di sini: FastCGI Process Manager (FPM) ; Tetapi fungsi itu secara khusus tidak didokumentasikan lebih lanjut dalam manual. Berikut kutipan dari PHP-FPM: PHP FastCGI Process Manager Wiki :
fastcgi_finish_request ()
Cakupan: fungsi php
Kategori: Optimasi
Fitur ini memungkinkan Anda untuk mempercepat implementasi beberapa query php. Akselerasi dimungkinkan jika ada tindakan dalam proses eksekusi skrip yang tidak memengaruhi respons server. Misalnya, menyimpan sesi di memcache dapat terjadi setelah halaman terbentuk dan diteruskan ke server web.
fastcgi_finish_request()
adalah fitur php, yang menghentikan keluaran respons. Server web segera mulai mentransfer respons "perlahan dan sedih" ke klien, dan php pada saat yang sama dapat melakukan banyak hal berguna dalam konteks kueri, seperti menyimpan sesi, mengubah video yang diunduh, menangani semua jenis statistik, dll.fastcgi_finish_request()
dapat memanggil menjalankan fungsi shutdown.Catatan:
fastcgi_finish_request()
memiliki kekhasan mana panggilan keflush
,print
atauecho
akan mengakhiri script awal.Untuk menghindari masalah itu, Anda dapat menelepon
ignore_user_abort(true)
tepat sebelum atau sesudahfastcgi_finish_request
panggilan:sumber
Versi lengkap:
sumber
Solusi yang lebih baik adalah melakukan proses latar belakang. Ini cukup mudah di unix / linux:
Anda harus melihat pertanyaan ini untuk contoh yang lebih baik:
PHP menjalankan proses latar belakang
sumber
Dengan asumsi Anda memiliki server Linux dan akses root, coba ini. Ini adalah solusi paling sederhana yang saya temukan.
Buat direktori baru untuk file berikut dan berikan izin penuh. (Kita bisa membuatnya lebih aman nanti.)
Taruh ini di file bernama
bgping
.Perhatikan
&
. Perintah ping akan berjalan di latar belakang sementara proses saat ini beralih ke perintah echo. Ini akan melakukan ping ke www.google.com 15 kali, yang akan memakan waktu sekitar 15 detik.Buat itu bisa dieksekusi.
Taruh ini di file bernama
bgtest.php
.Saat Anda meminta bgtest.php di browser Anda, Anda akan mendapatkan respons berikut dengan cepat, tanpa menunggu sekitar 15 detik hingga perintah ping selesai.
Perintah ping sekarang harus berjalan di server. Alih-alih perintah ping, Anda dapat menjalankan skrip PHP:
Semoga ini membantu!
sumber
Berikut modifikasi kode Timbo yang berfungsi dengan kompresi gzip.
sumber
Saya menggunakan host bersama dan siap
fastcgi_finish_request
untuk keluar dari skrip sepenuhnya. Saya juga tidak sukaconnection: close
solusinya. Menggunakannya memaksa koneksi terpisah untuk permintaan berikutnya, dengan biaya sumber daya server tambahan. Saya membacaTransfer-Encoding: cunked
Artikel Wikipedia dan mengetahui bahwa itu0\r\n\r\n
menghentikan tanggapan. Saya belum menguji ini secara menyeluruh di seluruh versi browser dan perangkat, tetapi berfungsi di semua 4 browser saya saat ini.sumber
Anda bisa mencoba melakukan multithreading.
Anda dapat menyiapkan skrip yang membuat panggilan sistem (menggunakan shell_exec ) yang memanggil biner php dengan skrip untuk melakukan pekerjaan Anda sebagai parameter. Tetapi menurut saya itu bukan cara yang paling aman. Mungkin Anda dapat mempererat hal-hal dengan chroot proses php dan hal lainnya
Sebagai alternatif, ada kelas di phpclass yang melakukan itu http://www.phpclasses.org/browse/package/3953.html . Tapi saya tidak tahu secara spesifik implementasinya
sumber
&
karakter untuk menjalankan proses di latar belakang.TL; DR Jawaban:
Fungsi Jawaban:
sumber
Masalah Anda dapat diselesaikan dengan melakukan beberapa pemrograman paralel di php. Saya mengajukan pertanyaan tentang itu beberapa minggu yang lalu di sini: Bagaimana seseorang dapat menggunakan multi threading dalam aplikasi PHP
Dan mendapat jawaban yang bagus. Saya sangat menyukainya. Penulis membuat referensi ke tutorial Pemrosesan Paralel Mudah di PHP (Sep 2008; oleh johnlim) yang sebenarnya dapat menyelesaikan masalah Anda dengan sangat baik karena saya telah menggunakannya untuk menangani masalah serupa yang muncul beberapa hari yang lalu.
sumber
Jawaban Joeri Sebrechts hampir saja, tetapi itu menghancurkan semua konten yang ada yang mungkin disangga sebelum Anda ingin memutuskan sambungan. Itu tidak memanggil
ignore_user_abort
dengan benar, memungkinkan skrip untuk dihentikan sebelum waktunya. Jawaban diyism bagus tetapi tidak berlaku secara umum. Misalnya, seseorang mungkin memiliki buffer keluaran yang lebih besar atau lebih sedikit yang tidak ditangani oleh jawaban itu, jadi itu mungkin tidak berfungsi dalam situasi Anda dan Anda tidak akan tahu mengapa.Fungsi ini memungkinkan Anda memutuskan sambungan kapan saja (selama header belum dikirim) dan mempertahankan konten yang telah Anda buat sejauh ini. Waktu pemrosesan tambahan tidak terbatas secara default.
Jika Anda juga membutuhkan memori tambahan, alokasikan sebelum memanggil fungsi ini.
sumber
Catatan untuk pengguna mod_fcgid (mohon, gunakan dengan resiko Anda sendiri).
Solusi Cepat
Jawaban yang diterima dari Joeri Sebrechts memang fungsional. Namun, jika Anda menggunakan mod_fcgid, Anda mungkin menemukan bahwa solusi ini tidak berfungsi dengan sendirinya. Dengan kata lain, ketika fungsi flush dipanggil, koneksi ke klien tidak ditutup.
The
FcgidOutputBufferSize
parameter konfigurasi mod_fcgid mungkin menyalahkan. Saya telah menemukan tip ini di:Setelah membaca penjelasan di atas, Anda mungkin sampai pada kesimpulan bahwa solusi cepat adalah menambahkan baris (lihat "Contoh Host Virtual" di bagian akhir):
di file konfigurasi Apache Anda (misalnya, httpd.conf), file konfigurasi FCGI Anda (misalnya, fcgid.conf) atau di file host virtual Anda (misalnya, httpd-vhosts.conf).
Detail & Solusi Kedua
Solusi di atas menonaktifkan buffering yang dilakukan oleh mod_fcgid baik untuk seluruh server atau untuk host virtual tertentu. Ini dapat menyebabkan hukuman kinerja untuk situs web Anda. Di sisi lain, ini mungkin tidak terjadi karena PHP melakukan buffering sendiri.
Jika Anda tidak ingin menonaktifkan buffering mod_fcgid , ada solusi lain ... Anda dapat memaksa buffer ini untuk mengosongkan .
Kode di bawah ini melakukan hal itu dengan mengembangkan solusi yang diusulkan oleh Joeri Sebrechts:
Apa yang pada dasarnya dilakukan oleh baris kode tambahan adalah mengisi buffer mod_fcgi , sehingga memaksanya untuk mengosongkan. Nomor "65537" dipilih karena nilai default
FcgidOutputBufferSize
variabel adalah "65536", seperti yang disebutkan di halaman web Apache untuk petunjuk terkait . Oleh karena itu, Anda mungkin perlu menyesuaikan nilai ini jika nilai lain ditetapkan di lingkungan Anda.Lingkungan Saya
Contoh Host Virtual
sumber
ini berhasil untuk saya
sumber
Ok, jadi pada dasarnya cara jQuery melakukan permintaan XHR, bahkan metode ob_flush tidak akan berfungsi karena Anda tidak dapat menjalankan fungsi di setiap onreadystatechange. jQuery memeriksa status, lalu memilih tindakan yang tepat untuk diambil (lengkap, kesalahan, sukses, batas waktu). Dan meskipun saya tidak dapat menemukan referensi, saya ingat mendengar bahwa ini tidak berfungsi dengan semua implementasi XHR. Sebuah metode yang saya percaya akan bekerja untuk Anda adalah persilangan antara polling ob_flush dan forever-frame.
Dan karena skrip dieksekusi sebaris, saat buffer dibilas, Anda mendapatkan eksekusi. Untuk membuat ini berguna, ubah console.log ke metode panggilan balik yang ditentukan dalam penyiapan skrip utama Anda untuk menerima data dan menindaklanjutinya. Semoga ini membantu. Salam, Morgan.
sumber
Solusi alternatifnya adalah menambahkan pekerjaan ke antrian dan membuat skrip cron yang memeriksa pekerjaan baru dan menjalankannya.
Saya harus melakukannya dengan cara itu baru-baru ini untuk menghindari batasan yang diberlakukan oleh host bersama - exec () dkk telah dinonaktifkan untuk PHP yang dijalankan oleh server web tetapi dapat dijalankan dalam skrip shell.
sumber
Jika
flush()
fungsi tidak berfungsi. Anda harus mengatur opsi berikutnya di php.ini seperti:sumber
Solusi Kerja Terbaru
sumber
Setelah mencoba banyak solusi berbeda dari utas ini (setelah tidak ada yang berhasil untuk saya), saya telah menemukan solusi di halaman resmi PHP.net:
sumber