L'API utilizza PHP 7.4, Laravel 8.0 e MySQL su Cloud SQL (il database gestito su Google Cloud). L'API deve gestire almeno 10.000 utenti concorrenti. L'immagine container che distribuiamo su Cloud Run ha nginx e PHP-FPM.
Ecco cosa ho fatto per migliorare i tempi di risposta. (Non preoccuparti se non tutto è immediatamente chiaro, spiegherò tutto nel dettaglio).
Allineare il numero di worker PHP-FPM all'impostazione di concorrenza massima su Cloud Run.
Configurare OPcache (compila e memorizza nella cache gli script PHP)
Migliorare le impostazioni di autoloading di Composer
Ottimizzazioni specifiche per Laravel, incluso il caching di route, view, eventi e l'utilizzo delle API resource
Allineare Concorrenza e Worker
L'applicazione usa nginx e PHP-FPM, un process manager per PHP. PHP è single-threaded, il che significa che un processo può gestire una (esattamente una) richiesta alla volta. PHP-FPM mantiene un pool di worker PHP (un worker è un processo) pronti a servire le richieste e ne aggiunge altri se la domanda aumenta. È buona pratica limitare la dimensione massima del pool di worker PHP-FPM, per garantire che l'utilizzo delle risorse (CPU e memoria) sia prevedibile.
Per configurare PHP-FPM al massimo delle prestazioni, ho prima impostato il tipo di process manager per PHP-FPM su static, in modo che il numero specificato di worker sia sempre in esecuzione e pronto a gestire le richieste. L'ho fatto copiando un file di configurazione personalizzato nel container dell'applicazione e configurando l'ambiente in modo che queste opzioni vengano rilevate da PHP-FPM (devi copiare la configurazione nel percorso atteso, nel mio caso /usr/local/etc/php-fpm.d/). Le impostazioni di cui avevo bisogno sono:
pm = static
pm.max_children = 10Tuttavia, se imposti un limite e arrivano al server più richieste di quante il pool possa gestire, le richieste iniziano a fare coda, il che aumenta il tempo di risposta per quelle richieste:

Limitare le Richieste Concorrenti su Cloud Run
Per evitare il queueing delle richieste in nginx, dovrai limitare il numero di richieste che Cloud Run invia al tuo container contemporaneamente.
Cloud Run utilizza l'autoscaling basato sulle richieste. Questo significa che limita la quantità di richieste concorrenti che invia a un container e aggiunge più container se tutti i container hanno raggiunto il loro limite. Puoi modificare quel limite con l'impostazione di concorrenza. L'ho impostato a 10, che ho determinato essere il numero massimo di richieste concorrenti che un container con 1 GB di memoria e 1 vCPU può gestire per questa applicazione.

Assicurati davvero che l'impostazione di concorrenza di Cloud Run corrisponda al numero massimo di worker PHP-FPM! Ad esempio, se Cloud Run invia 100 richieste concorrenti a un container prima di aggiungerne altri, e hai configurato PHP-FPM per avviare solo 10 worker, vedrai molte richieste in coda.
Se queste modifiche non sono sufficienti per raggiungere le prestazioni desiderate, controlla le metriche di Cloud Run per vedere quali sono le percentuali di utilizzo effettivo. Potresti dover aumentare la quantità di memoria e vCPU disponibili per il container. Il lato negativo di questa ottimizzazione è che verranno eseguiti più container a causa della minore concorrenza, con conseguenti costi più elevati. Ho anche notato ritardi temporanei all'avvio di nuove istanze, ma questi si normalizzano nel tempo.
Configurare OPCache
OPCache è un'estensione PHP predefinita che memorizza nella cache gli script compilati in memoria, migliorando notevolmente i tempi di risposta. Ho abilitato e regolato le impostazioni di OPCache aggiungendo le opzioni dell'estensione a un file php.ini personalizzato (nel mio caso l'ho messo nella directory /usr/local/etc/php/conf.d/). Di seguito è riportata una configurazione generica facilmente riutilizzabile; puoi fare riferimento alla documentazione per i dettagli su ogni opzione.
opcache.enable=1
opcache.memory_consumption=128
opcache.interned_strings_buffer=64
opcache.max_accelerated_files=32531
opcache.validate_timestamps=0
opcache.save_comments=1
opcache.fast_shutdown=0Ottimizzare Composer
Composer è un dependency manager per PHP. Ti permette di specificare le librerie di cui la tua app ha bisogno e le scarica in una directory. Genera anche un file di configurazione per l'autoload, che mappa i percorsi di import ai file. Se passi il flag --optimize-autoloader a Composer, genererà questo file una sola volta e non lo aggiornerà dinamicamente se aggiungi nuovo codice. Sebbene ciò sia comodo in sviluppo (le modifiche vengono applicate immediatamente), in produzione può rendere il codice molto lento.
Puoi ottimizzare l'autoloader di Composer passando il flag --optimize-autoloader in questo modo:
composer install --optimize-autoloader --no-devOttimizzazioni specifiche per Laravel
L'applicazione che ho ottimizzato è costruita con Laravel, che fornisce una serie di strumenti per migliorare le prestazioni dell'API. Ecco cosa ho fatto in aggiunta alle altre modifiche per portare i tempi di risposta sotto i 100ms.
Ho sfruttato le funzionalità di caching integrate di Laravel durante le build per ridurre i tempi di avvio. Queste modifiche non hanno svantaggi, eccetto il fatto che non potrai usare route definite con closure (non possono essere messe in cache). Puoi mettere in cache view, eventi e route con questi comandi:
php artisan view:cache
php artisan event:cache
php artisan route:cacheEvita di eseguire php artisan config:cache poiché Laravel ignora le variabili d'ambiente se metti in cache la configurazione.
L'utilizzo delle API Resource di Laravel migliora ulteriormente i tempi di risposta dell'applicazione. Si è dimostrato molto più veloce rispetto al lasciare che il framework converta automaticamente singoli oggetti e collection in JSON.
Riepilogo
In questo post ho condiviso ciò che ho imparato ottimizzando le prestazioni di un'API basata su PHP su Cloud Run. Tutte le modifiche insieme hanno contribuito a ridurre i tempi di risposta a un decimo del risultato originale, e penso che l'impatto maggiore sia stato dato dall'allineamento di concorrenza e worker PHP-FPM (se hai poco tempo, fai solo questo). Monitorare le metriche dell'applicazione è stato fondamentale durante tutta la fase di performance testing, così come ispezionare i log di Cloud Run dopo ogni modifica.
Se la tua applicazione mostra ancora prestazioni scarse dopo questi cambiamenti, ci sono altre modifiche che puoi apportare per migliorare i tempi di risposta, che non ho discusso qui.
Aumenta i limiti di memoria PHP se necessario
Controlla MySQL per le query lente (spesso dovute a indici mancanti)
Metti in cache le risposte con una CDN
Migra a PHP 8.0 (fino a 3x più veloce)
lucavallin

