Preload di php 7.4: Komposer dan pilih file untuk preload

Kami di Badoo aktif beralih ke PHP 7.4 dan sangat antusias tentang peluang untuk menggunakan fungsi preload baru. Belum lama ini kami membicarakan eksperimen kami dengannya.

Tampaknya, komunitas sama bersemangatnya dengan kami. Pengembang framework secara aktif mendiskusikan kemungkinan memperkenalkan preload (dan beberapa telah membuat dukungannya ). Sekarang giliran manajer ketergantungan Composer.



Italo Baeza menulis sebuah artikel di mana dia menyatakan pendapatnya tentang bagaimana Composer harus bekerja dengan preload. Saya memutuskan untuk membagikan terjemahan dari teks ini, dan pada saat yang sama terjemahan dari artikelnya yang lain , tentang bagaimana para pengembang Komposer sendiri menjawab proposal tersebut, serta tentang alat baru yang membuat bekerja dengan preload lebih mudah.

Bagaimana Komposer Harus Dimuat di PHP 7.4


Preload adalah salah satu fitur penting yang ditawarkan PHP 7.4 kepada pengembang yang membutuhkan kinerja yang lebih baik. Fungsi ini dapat disebut "pemanasan" sebelum menerapkan mesin JIT, yang akan muncul (atau akan muncul) di PHP 8. Sebelum itu, preloading akan cukup, dan siapa tahu, mungkin mereka dapat bekerja bersama-sama.

Apa fungsi preload dijelaskan dalam artikel ini . Intinya sangat sederhana: php.ini menentukan skrip PHP yang ketika proses dimulai, file dimuat ke dalam memori (preload). Dalam kombinasi dengan OPCache dan fungsi autoloader , file Composer juga dapat dikompilasi dan ditautkan satu kali, setelah itu mereka akan tersedia untuk semua permintaan berikutnya. Berkat ini, PHP tidak perlu mengunduh dan mengkompilasi file dengan setiap permintaan.

Namun, pengembang Komposer tidak setuju tentang bagaimana seharusnya membantu preload, selain menyediakan fungsi startup. Faktanya adalah sebagai berikut:

  • preloading pertama kali diumumkan dalam PHP 7.4;
  • tidak ada arahan Komposer untuk membantu memuat file;
  • untuk melakukan preload, Anda memerlukan akses ke php.ini, yaitu ke proses itu sendiri;
  • preloading semua file tidak serta merta meningkatkan kinerja dibandingkan dengan preloading hanya file yang paling banyak diminta.

Dengan kata lain, hanya mereka yang memiliki akses penuh ke server yang dapat menggunakan preloading. Ini tidak termasuk server bersama dan beberapa solusi PaaS yang tidak melibatkan bekerja dengan php.ini.

Jadi, bagaimana Composer dapat membantu memuat sebelumnya mengingat ini adalah sebuah inovasi? Ini pendapat saya.

Bagaimana preload seharusnya bekerja


Mekanisme untuk preloading harus didasarkan pada daftar file yang akan dimuat dan disimpan dalam memori saat startup. Dan karena ini adalah daftar, kita perlu bekerja dengan berbagai file dan membiarkan Komposer melakukan semua pekerjaan, daripada memuat setiap file secara manual.

Composer harus mengambil daftar file yang ditentukan oleh aplikasi (proyek root) dan mengkompilasi semuanya menjadi file yang dapat digunakan PHP tanpa kesulitan.
Pada saat yang sama, kita membutuhkan kemampuan untuk menambah dan menghapus paket dari mekanisme preload.
Preloading tidak boleh bekerja di tingkat paket, karena itu adalah tanggung jawab pengembang untuk mengaktifkan atau menonaktifkan preloading setiap paket.

Preloading dalam Komposer harus opsional. Pengembang harus dapat menonaktifkannya sehingga PHP menggunakan preloadernya sendiri, yang dapat bekerja berdasarkan analisis OPCache - tergantung pada beban aplikasi dan bekerja jauh lebih efisien daripada hanya preloading semua file.

Semuanya dimulai di preload.json


Agar tidak menyulitkan sistem, letakkan file preload.json di root proyek. Ini akan mencantumkan file preload yang dapat dipilih oleh Composer. Karena ini adalah file JSON, pengembang bahkan dapat membuatnya dengan bantuan perintah khusus. Saya pikir akan bagus jika Komposer memiliki utilitas untuk membuat file JSON berbasis script.

{ "pre-compile": [ "my-script.php", "my-other-script.php" ], "extensions": [ "php" ], "files": [ "app/*", "config/", "helpers.php", "app/Models/*", "app/Controllers/*/Http/*", "app/Views/Compiled*.php" ], "namespace": [ "App\\Models", "App\\Controllers\\", "App\\Views\\MainView", "Vendor\\Package\\*", ], "packages": { "symfony/http-client": true, "robert/*-client": true, "vendor/package": { "files": true, "namespace": true }, "foo/bar": { "files": [ "helpers.php", "loaders/*" ], "namespace": [ "Foo\\Bar\\DynamicLoaders\\*", "Foo\\Bar\\Clients" ] } }, "output": "preload-compiled.php" } 

Menggunakan preload.json memungkinkan Anda untuk dengan cepat memeriksa apakah preloading termasuk dalam proyek: jika file tersebut hilang, maka preloading tidak didukung atau tidak diinginkan.

Mari kita lihat apa yang dilakukan kunci.

pra-kompilasi

File-file ini akan dieksekusi oleh Komposer. Setiap skrip harus mengembalikan array jalur file absolut untuk menambahkannya ke daftar preload, yang akan memainkan peran daftar utama.

 "pre-compile": [ "my-script.php", "my-other-script.php" ] 

File-file ini akan dieksekusi dalam urutan yang ditentukan.

Tujuannya adalah agar pengembang membuat daftar file yang menurutnya sesuai, daripada mengandalkan file JSON tunggal. File-file ini akan dieksekusi terlebih dahulu. Dan ya, Anda hanya dapat mengimplementasikan preload.json dengan kunci ini. Karena kita berbicara tentang file PHP, ketika menyusun array, Anda bahkan dapat menambahkan file lain.

ekstensi

Ini adalah daftar ekstensi file yang harus dimuat sebelumnya. Secara default, hanya file dengan ekstensi php yang diambil.

 "extensions": ["php", "php5", "php7"] 

Misalnya, Anda dapat menambahkan direktori yang diisi dengan file * .phtml, termasuk beberapa file PHP yang berguna, dan Composer hanya akan memilihnya, dan bukan seluruh isi direktori.
Seperti yang Anda pahami, proses ini dapat diganti dengan menambahkan file secara manual.

file

Kunci ini memberitahu Composer untuk mengunduh semua file dari daftar yang jalurnya relatif terhadap lokasi composer.json.

 "files": [ "helpers.php", "app/Models/*", "app/Controllers/*/Http/*", "app/Views/Compiled*.php", ] 

Mengetahui daftar itu mudah:

  • gunakan jalur relatif untuk menambahkan file dan direktori;
  • dari direktori hanya file anak yang disimpan di dalamnya yang akan ditambahkan (tidak secara rekursif);
  • jalur rekursif ditandai dengan tanda bintang (*);
  • menggunakan simbol ini Anda juga dapat, misalnya, menambahkan file dan direktori tertentu: src/Clients/*/Stores atau src/Model*.php .

Menambahkan file dengan mask tanpa memilih secara manual atau membuat skrip khusus aplikasi sangat berguna saat mengembangkan aplikasi besar.

Jika Anda hanya perlu memuat semua file menggunakan kunci autoload di file JSON Composer, atur menjadi true .

namespace

Kunci ini memberitahu Composer untuk memuat file dengan namespace atau nama kelas yang diberikan seperti file atau directory . Mekanisme yang sama memungkinkan Anda untuk memanggil nama ruang secara dinamis dari paket yang diinstal lainnya.

 "namespaces": [   "App\\Models",   "App\\Controllers\\",   "App\\Views\\MainView",   "Vendor\\Package\\*", ] 

Ini juga nyaman ketika bekerja pada aplikasi besar yang lebih bergantung pada ruang nama, daripada pada file yang dapat berubah kapan saja. Composer akan secara otomatis mengekstrak file sesuai dengan namespace dan memasukkannya ke dalam daftar.

paket

Kunci ini memungkinkan Anda untuk memuat file lain yang terdaftar dari paket eksternal, seperti file tambahan atau kelas yang terkait dengan namespace.

 "packages": {   "symfony/http-client": true,   "robert/*-client": true,   "vendor/package": {       "files": true,       "namespace": true   },   "foo/bar": {       "files": {           "helpers.php",           "loaders/*"       },       "namespace": [           "Foo\\Bar\\DynamicLoaders\\*",           "Foo\\Bar\\Clients"       ]   } } 

Semuanya sangat sederhana di sini: jika nilainya benar, maka seluruh isi kunci autoload dalam file composer.json dari paket ini akan dimuat. Jika tidak, Anda dapat lebih mengontrol penambahan preload.

Jika nilai kunci benar, maka itu akan mengunduh semua file yang terdaftar dalam autoload . Nilai standarnya false . Ini juga berlaku untuk kunci namespace .

Anda juga dapat memilih file individu atau ruang nama dengan aturan ini. Tetapi dalam kasus ini, kunci autoload tidak akan digunakan.

keluaran

Ini hanyalah nama file daftar preload yang dikompilasi.

 "output": "preload-compiled.php" 

Perakitan mudah


Daftar pra-muat kami siap, dan kami dapat memanggil Komposer untuk mengkompilasi skrip pra-muat utama.

 composer preload 

Akibatnya, preload-compiled.php akan dibuat dengan semua file yang harus dimuat oleh PHP. Tentu saja, Anda dapat mengubah nama file sesuai keinginan.

Anda juga perlu mengganti kunci preload parameter.

 composer preload \   --input=my-custom-preload-list.json \   --output=my-preload.php 

Dinonaktifkan secara default


Proyek tanpa preload.json akan mengembalikan kesalahan jika Anda mencoba mengkompilasi file untuk preloading. Alasannya adalah bahwa Komposer tidak akan (dan tidak seharusnya) menebak apa yang harus dimuat.

Biarkan saya mengingatkan Anda bahwa preload tidak mengganggu fungsi Composer yang normal. Karena ini adalah perintah konsol, dengan pengembangan lokal, Anda dapat sepenuhnya mengabaikan preloading. Satu-satunya hal yang dibutuhkan mekanisme preload Composer adalah file Autoload, yang harus dihasilkan jika hilang. Yah, setelah semua, hampir tahun 2020 di halaman, PSR-4 digunakan di mana-mana, kan?

Hasil


Anda harus mendapatkan file php dengan sesuatu seperti ini:

 <?php /** * Preloading @generated by Composer */ // Autoload the classes so those can be preloaded using `require_once`. require_once __DIR__.'/../autoload.php'; // File list $files = [ '/var/www/app/Foo.php', '/var/www/app/Bar.php', '/var/www/helpers/basic.php', '/var/www/helpers/advanced.php', '/var/www/vendor/Foo/Bar/src/Class.php', '/var/www/vendor/Foo/Bar/helpers/helpers.php', '/var/www/vendor/Foo/Bar/config.php', // ... ]; // Preload all root project files foreach ($files as $file) { require_once $file; } 

Bahkan, ini hanya daftar file yang akan dimuat menggunakan fungsi autoloader di Composer. PHP akan mengeksekusi file ini sekali, dan itu akan menjadi sejarah.


Saya sangat berharap bahwa Komposer akan memiliki beberapa cara untuk memuat file tanpa harus menulis peretasan.

Karena metode yang dijelaskan di atas bukan bagian dari kernel Komposer, Anda masih dapat memilih file yang paling penting untuk preloading berdasarkan analisis OPCache tanpa menyentuh yang kurang dibutuhkan. Bayangkan bahwa alih-alih melakukan prapembuatan 1.500 file dengan kapasitas 100 MB, Anda dapat mengunduh hanya 150 file dengan kapasitas 10 MB, sambil mempertahankan 99% dari kinerja aslinya.

Kami memuat proyek PHP 7.4 dalam satu baris


Segera setelah saya menulis artikel tentang bagaimana Komposer dapat membantu Anda melakukan preload proyek, Seldaek (anggota tim pengembangan Komposer) membunuh semua harapan bahwa Komposer akan memiliki pilihan yang mudah untuk memuatkan proyek ke dalam proses PHP dari manajer paket.

(...) Saya akan menjelaskan: Saya yakin bahwa dalam waktu dekat kami tidak akan menambahkan apa pun yang terkait dengan prapembuatan ke Komposer.

Mengapa Preloading dalam PHP lebih merupakan masalah pengembangan (bukan ketergantungan), diselesaikan dengan mengedit secara manual php.ini - hanya pengembang yang dapat melakukannya jika mereka sendiri yang mengelola PHP.

Tetapi ini tidak menghentikan saya untuk membuat paket sendiri untuk melakukan preload proyek. Dan kamu juga.

Preload dan Metrik


Preloading dapat menjadi alat yang baik untuk peningkatan produktivitas yang sederhana dan murah tanpa pemrosesan serius.

Tapi masalahnya bukan bagaimana melakukan preload, tapi apa . Preloading seluruh kerangka kerja dan ribuan file akan dengan cepat menghabiskan memori, sehingga melakukannya secara membabi buta bukanlah suatu pilihan, setidaknya dalam proyek-proyek besar. Dianjurkan untuk hanya mengunduh file yang paling banyak diminta. Tetapi bagaimana cara mendefinisikannya?

Untungnya, OPCache memungkinkan opcache_get_status () untuk mengumpulkan data tentang file mana yang paling banyak diakses. Anda tidak hanya dapat mengetahui file mana yang paling diminati, tetapi bahkan berapa banyak memori yang mereka konsumsi beberapa saat setelah aplikasi dimulai.

Dianjurkan untuk menunggu seminggu atau sampai saat OPCache mendaftarkan sejumlah hit. Itu semua tergantung pada aplikasi, tetapi Anda mendapatkan intinya.
Jadi mari kita buat daftar preload berdasarkan statistik dari file paling populer. Saya membuat paket untuk ini.

Bayangkan ... Preloader!


Paket ini secara otomatis akan membuat daftar preload untuk aplikasi Anda. Ini akan mengumpulkan statistik penggunaan OPCache, mengurutkan file dengan jumlah klik dan membuat daftar sehingga ukuran file total tidak melebihi ambang yang ditentukan.



Saya bingung untuk waktu yang lama dalam mencari strategi terbaik untuk membuat daftar. Dan saya sampai pada kesimpulan bahwa yang terbaik adalah menambahkan semua file ke dalamnya sampai Anda mengalami batas memori, yang untuk paket adalah 32 MB secara default. File akan diurutkan berdasarkan jumlah klik, dan paket itu sendiri akan secara otomatis dikecualikan.

Dengan kata lain, PHP akan meningkatkan kinerja aplikasi saat memproses sebagian besar permintaan.

Dan bagaimana cara menggunakannya? Beri tahu Pembuat Komposer tempat menulis skrip Prapuat, dan Anda selesai.

 use DarkGhostHunter\Preloader\Preloader; Preloader::make() ->autoload('vendor/autoload.php') ->output('preload.php') ->generate(); 

Tentu saja, Anda harus memilih kapan untuk menghasilkan, tetapi itulah intinya. Anda bahkan dapat melakukan ini secara acak dan menulis ulang daftar, misalnya, untuk setiap permintaan ke-100.

 use DarkGhostHunter\Preloader\Preloader; Preloader::make() ->whenOneIn(100) ->autoload('vendor/autoload.php') ->output('preload.php') ->overwrite() ->generate(); 

Anda akan mendapatkan skrip preload yang sudah jadi yang bisa Anda masukkan ke dalam php.ini.

 <?php /** * This file is generated automatically by Preloader. * * This script uses Composer Autoload file and `require_once` to preload the files in this * list. Add this file to your `php.ini` in `opcache.preload` to preload this list into * PHP at startup. Additionally, this file also includes information about Opcache. * * * Add (or update) this line in `php.ini`: * * opcache.preload=/www/app/vendor/preload.php * * --- Config --- * Generated at: 2019-11-20 15:20:49 UTC * Opcache * - Used Memory: 130585 B * - Free Memory: 294896 B * - Wasted Memory: 347764 B * - Cached files: 2675 * - Hit rate: 94% * - Misses: 542 * Preloader config * - Memory limit: 32 MB * - Overwrite: false * - Files excluded: 0 * - Files appended: 0 */ require_once '/www/app/vendor/autoload.php'; $files = [ '/www/app/ClassFoo.php', '/www/app/ClassBar.php', '/www/app/ClassBaz.php', '/www/app/ClassQuz.php', '/www/app/ClassQux.php', '/www/app/vendor/author/package/src/Example.php', // ... ]; foreach ($files as $file) { require_once $file; } 

Dan itu dia. Cobalah sendiri: darkghosthunter / preloader - Packagist .

Source: https://habr.com/ru/post/id480746/


All Articles