Cache nginx: semuanya baru - lama terlupakan

Dalam kehidupan setiap proyek, saatnya tiba ketika server berhenti untuk memenuhi persyaratan SLA dan secara harfiah mulai tersedak jumlah lalu lintas yang masuk. Setelah itu, proses panjang untuk menemukan kemacetan, pertanyaan berat, indeks yang salah dibuat, data yang tidak di-cache, atau sebaliknya, terlalu sering memperbarui data dalam cache dan sisi gelap lain dari proyek, dimulai.

Tapi apa yang harus dilakukan ketika kode Anda "sempurna", semua permintaan berat ditempatkan di latar belakang, semua yang mungkin di-cache, dan server masih belum mencapai indikator SLA yang kita butuhkan? Jika memungkinkan, maka tentu saja Anda dapat membeli mobil baru, mendistribusikan beberapa lalu lintas dan melupakan masalah untuk sementara waktu.

Tetapi jika Anda merasa bahwa server Anda mampu melakukan lebih, atau ada parameter ajaib yang mempercepat situs sebanyak 100 kali, maka Anda dapat mengingat fitur nginx bawaan yang memungkinkan Anda untuk menembolok respons dari backend. Mari kita lihat apa itu dan bagaimana hal itu dapat membantu meningkatkan jumlah permintaan yang diproses oleh server.

Apa itu cache Nginx dan bagaimana cara kerjanya?


Cache nginx dapat secara signifikan mengurangi jumlah permintaan untuk backend. Ini dicapai dengan menyimpan respons HTTP untuk waktu tertentu, dan ketika mengakses sumber daya lagi, mengembalikannya dari cache tanpa mem-proxy-kan permintaan backend. Caching, bahkan untuk waktu yang singkat, akan memberikan peningkatan signifikan pada jumlah permintaan yang diproses oleh server.

Sebelum melanjutkan dengan konfigurasi nginx, Anda perlu memastikan bahwa itu dibangun dengan modul “ngx_http_proxy_module”, karena kami akan mengonfigurasinya menggunakan modul ini.

Untuk kenyamanan, Anda dapat meletakkan konfigurasi dalam file yang terpisah, misalnya, “/etc/nginx/conf.d/cache.conf”. Mari kita lihat direktif proxy_cache_path, yang memungkinkan Anda untuk mengonfigurasi pengaturan penyimpanan cache.

proxy_cache_path /var/lib/nginx/proxy_cache levels=1:2 keys_zone=proxy_cache:15m max_size=1G; 

"/ Var / lib / nginx / proxy_cache" menentukan jalur penyimpanan cache di server. Di direktori ini nginx akan menyimpan file-file yang sangat dengan respon dari backend. Pada saat yang sama, nginx tidak akan secara mandiri membuat direktori untuk cache, Anda harus mengurusnya sendiri.

"Levels = 1: 2" - mengatur tingkat sarang direktori dengan cache. Level Nesting ditunjukkan melalui “:”, dalam hal ini 2 direktori akan dibuat, total 3 level sarang diperbolehkan. Untuk setiap tingkat bersarang, nilai dari 1 hingga 2 tersedia, yang menunjukkan cara membuat nama direktori.

Poin penting adalah bahwa nama direktori tidak dipilih secara acak, tetapi dibuat berdasarkan nama file. Nama file, pada gilirannya, adalah hasil dari fungsi md5 dari kunci cache, kita akan melihat kunci cache sedikit kemudian.

Mari kita lihat dalam praktik bagaimana path ke file cache dibangun:

 /var/lib/nginx/proxy_cache/2/49/07edcfe6974569ab4da6634ad4e5d492 

Parameter “Keys_zone = proxy_cache: 15m” menetapkan nama zona dalam memori bersama, tempat semua kunci aktif dan informasi tentangnya disimpan. Melalui “:” menunjukkan ukuran memori yang dialokasikan dalam MB. Menurut nginx, 1 MB sudah cukup untuk menyimpan 8 ribu kunci.

"Max_size = 1G" menentukan ukuran cache maksimum untuk semua halaman di atas yang nginx akan menghapus data yang kurang dibutuhkan.

Dimungkinkan juga untuk mengontrol masa pakai data dalam cache, karena ini cukup untuk menentukan parameter "tidak aktif" dari direktif "proxy_cache_path", yang secara default adalah 10 menit. Jika selama waktu yang ditentukan dalam parameter "tidak aktif" tidak ada panggilan ke data cache, maka data ini dihapus bahkan jika cache belum "masam".

Seperti apa cache ini? Ini sebenarnya adalah file biasa di server, yang isinya ditulis:

• kunci cache;
• header cache;
• respons konten dari backend.

Jika semuanya jelas dengan header dan respons dari backend, maka ada sejumlah pertanyaan pada "kunci cache". Bagaimana itu dibangun dan bagaimana bisa dikelola?

Untuk menjelaskan templat untuk membuat kunci cache di nginx, ada direktif proxy_cache_key, di mana string ditentukan sebagai parameter. Sebuah string dapat terdiri dari variabel apa saja yang tersedia di nginx.

Sebagai contoh:

 proxy_cache_key $request_method$host$orig_uri:$cookie_some_cookie:$arg_some_arg; 

Simbol ":" antara parameter cookie dan parameter get digunakan untuk mencegah tabrakan antara kunci cache, Anda dapat memilih simbol lain dari pilihan Anda. Secara default, nginx menggunakan baris berikut untuk menghasilkan kunci:

 proxy_cache_key $scheme$proxy_host$request_uri; 

Arahan berikut harus diperhatikan yang akan membantu Anda mengelola caching Anda secara lebih fleksibel:

proxy_cache_valid - Menentukan waktu caching respons. Dimungkinkan untuk menunjukkan status spesifik dari respons, misalnya 200, 302, 404, dll., Atau untuk menentukan semuanya sekaligus menggunakan konstruk “apa saja”. Jika hanya waktu caching yang ditentukan, nginx akan default untuk cache hanya 200, 301 dan 302 status.

Contoh:

 proxy_cache_valid 15m; proxy_cache_valid 404 15s; 

Dalam contoh ini, kami menetapkan masa pakai cache menjadi 15 menit untuk status 200, 301, 302 (nginx menggunakannya secara default, karena kami tidak menentukan status tertentu). Baris berikutnya mengatur waktu caching ke 15 detik, hanya untuk tanggapan dengan status 404.

proxy_cache_lock - Arahan ini akan membantu untuk menghindari beberapa lintasan ke backend segera setelah set cache, cukup atur nilai pada posisi "on". Semua permintaan lain akan menunggu respons dalam cache, atau batas waktu untuk memblokir permintaan ke halaman. Dengan demikian, semua batas waktu dapat dikonfigurasi.

proxy_cache_lock_age - Memungkinkan Anda untuk menetapkan batas waktu untuk tanggapan dari server, setelah itu permintaan berikutnya akan dikirim kepadanya setelah cache telah ditetapkan. Standarnya adalah 5 detik.

proxy_cache_lock_timeout - Menetapkan waktu untuk menunggu kunci, setelah itu permintaan akan dikirim ke backend, tetapi jawabannya tidak akan di-cache. Standarnya adalah 5 detik.

proxy_cache_use_stale - Arahan lain yang berguna yang memungkinkan Anda untuk mengkonfigurasi ketika dimungkinkan untuk menggunakan cache yang usang.

Contoh:

 proxy_cache_use_stale error timeout updating; 

Dalam hal ini, ia akan menggunakan cache yang ketinggalan jaman jika terjadi kesalahan koneksi, mengirim permintaan, membaca respons dari server, melebihi batas tunggu untuk mengirim permintaan, membaca respons dari server, atau jika data dalam cache diperbarui pada saat permintaan.

proxy_cache_bypass - Menentukan kondisi di mana nginx tidak akan mengambil respons dari cache, tetapi segera mengarahkan permintaan ke backend. Jika setidaknya salah satu parameter tidak kosong dan tidak sama dengan "0". Contoh:

 proxy_cache_bypass $cookie_nocache $arg_nocache; 

proxy_no_cache - Menetapkan kondisi di mana nginx tidak akan menyimpan respons dari backend ke cache. Prinsip operasi adalah sama dengan yang ada pada direktif proxy_cache_bypass.

Kemungkinan masalah dengan caching halaman


Seperti disebutkan di atas, bersama dengan caching respons HTTP, nginx menyimpan header yang diterima dari backend. Jika situs Anda menggunakan sesi, maka cookie sesi juga akan di-cache. Semua pengguna yang mengunjungi halaman yang beruntung Anda cache akan menerima data pribadi Anda yang disimpan dalam sesi.

Tantangan berikutnya yang akan Anda hadapi adalah manajemen caching. Tentu saja, Anda dapat mengatur waktu cache tidak signifikan 2-5 menit dan ini akan cukup dalam kebanyakan kasus. Tapi ini tidak berlaku di semua situasi, jadi kami akan menemukan kembali sepeda kami. Sekarang, hal pertama yang pertama.

Manajemen Pelestarian Cookie

Caching di sisi nginx membebankan beberapa batasan desain. Misalnya, kita tidak bisa menggunakan sesi pada halaman dalam cache, karena pengguna tidak mencapai backend, batasan lain adalah pengiriman cookie oleh backend. Karena nginx cache semua header, untuk menghindari menyimpan sesi orang lain di cache, kita perlu melarang pengiriman cookie untuk halaman yang di-cache. Arahan proxy_ignore_headers akan membantu kami dalam hal ini. Argumen daftar header yang harus diabaikan dari backend.

Contoh:

 proxy_ignore_headers "Set-Cookie"; 

Dengan baris ini, kami mengabaikan pemasangan cookie dari server proksi, yaitu, pengguna akan menerima respons tanpa tajuk "Set-Cookies". Dengan demikian, segala sesuatu yang dicoba dituliskan oleh backend ke cookie akan diabaikan di sisi klien, karena ia bahkan tidak akan tahu bahwa itu dimaksudkan untuk sesuatu. Pembatasan cookie ini harus dipertimbangkan ketika mengembangkan aplikasi. Misalnya, untuk meminta otorisasi, Anda dapat mematikan kunci kontak sehingga pengguna menerima cookie sesi.

Anda juga harus mempertimbangkan seumur hidup sesi, itu dapat dilihat dalam parameter “ session.gc_maxlifetime ” dari konfigurasi php.ini. Bayangkan bahwa pengguna masuk ke situs dan mulai melihat feed berita, semua data sudah ada di cache nginx. Setelah beberapa waktu, pengguna memperhatikan bahwa otorisasi-nya telah hilang dan ia harus melalui proses otorisasi, meskipun selama ini ia berada di situs, menonton berita. Ini terjadi karena pada semua permintaannya nginx mengembalikan hasil dari cache tanpa mengirim permintaan ke backend. Oleh karena itu, backend memutuskan bahwa pengguna tidak aktif dan setelah waktu yang ditentukan dalam " session.gc_maxlifetime " menghapus file sesi.

Untuk mencegah hal ini terjadi, kami dapat meniru permintaan backend. Misalnya, melalui ajax mengirim permintaan yang akan dijamin lolos ke backend. Untuk meneruskan cache nginx ke backend, cukup kirim permintaan POST, Anda juga dapat menggunakan aturan dari direktif “proxy_cache_bypass”, atau cukup nonaktifkan cache untuk halaman ini. Permintaan tidak harus memberikan sesuatu kembali, itu bisa berupa file dengan satu baris memulai sesi. Tujuan dari permintaan semacam itu adalah untuk memperpanjang masa pakai sesi ketika pengguna berada di situs, dan nginx dengan sadar memberikan data yang di-cache ke semua permintaannya.

Manajemen siram cache

Pertama, Anda perlu menentukan persyaratan, tujuan apa yang ingin kami capai. Katakanlah situs kami memiliki bagian dengan siaran teks acara olahraga populer. Saat memuat halaman diberikan dari cache, maka semua pesan baru datang di soket. Agar pengguna dapat melihat pesan saat ini pada saat saat ini pada saat boot pertama, daripada 15 menit yang lalu, kita harus dapat menghapus cache nginx secara mandiri kapan saja. Pada saat yang sama, nginx mungkin tidak berada pada mesin yang sama dengan aplikasi. Juga, salah satu persyaratan untuk reset adalah kemampuan untuk menghapus cache, di beberapa halaman sekaligus.

Sebelum Anda mulai menulis solusi Anda, mari kita lihat apa yang ditawarkan nginx di luar kotak. Untuk mengatur ulang cache, nginx memiliki arahan khusus yang disebut “proxy_cache_purge”, yang mencatat kondisi untuk mengatur ulang cache. Kondisi sebenarnya adalah garis normal, yang, jika tidak kosong dan bukan "0", akan menghapus cache dengan kunci yang dilewati. Pertimbangkan sebuah contoh kecil.

 proxy_cache_path /data/nginx/cache keys_zone=cache_zone:10m; map $request_method $purge_method { PURGE 1; default 0; } server { ... location / { proxy_pass http://backend; proxy_cache cache_zone; proxy_cache_key $uri; proxy_cache_purge $purge_method; } } 

Contoh diambil dari situs web nginx resmi.

Variabel $ purge_method bertanggung jawab untuk membilas cache, yang merupakan syarat untuk direktif proxy_cache_purge dan diset ke 0 secara default. Ini berarti bahwa nginx berfungsi dalam mode "normal" (ini menyimpan respons dari backend). Tetapi jika Anda mengubah metode permintaan menjadi "PURGE", maka alih-alih mem-proxy permintaan backend dengan menyimpan respons, entri cache akan dihapus menggunakan kunci cache yang sesuai. Dimungkinkan juga untuk menentukan mask penghapusan dengan menentukan "*" di akhir kunci cache. Jadi, kita tidak perlu tahu lokasi cache pada disk dan prinsip pembentukan kunci, nginx mengambil tanggung jawab ini. Namun ada juga kelemahan dari pendekatan ini.

  • Arahan proxy_cache_purge tersedia sebagai bagian dari langganan komersial.
  • Hanya dimungkinkan untuk menghapus cache secara langsung, atau dengan menggunakan mask form {cache key} “*”

Karena alamat halaman yang di-cache bisa sangat berbeda, tanpa bagian umum, pendekatan dengan mask “*” dan arahan “proxy_cache_purge” tidak cocok untuk kami. Masih mengingat sedikit teori dan menemukan ide favorit Anda.

Kita tahu bahwa cache nginx adalah file biasa di server. Kami secara independen menentukan direktori untuk menyimpan file cache dalam direktif “proxy_cache_path”, kami bahkan menentukan logika pembentukan path ke file dari direktori ini menggunakan “level”. Satu-satunya hal yang kami lewatkan adalah pembentukan kunci caching yang benar. Tapi kita juga bisa melihatnya di direktif “proxy_cache_key”. Sekarang yang harus kita lakukan adalah:

  • membentuk path lengkap ke halaman, persis seperti yang ditentukan dalam direktif proxy_cache_key;
  • menyandikan string yang dihasilkan di md5;
  • buat direktori bersarang menggunakan aturan dari parameter "level".
  • Dan sekarang kita sudah memiliki path lengkap ke file cache di server. Sekarang yang tersisa bagi kita adalah menghapus file ini. Dari bagian pengantar, kita tahu bahwa nginx mungkin tidak terletak di mesin aplikasi, jadi Anda harus memungkinkan untuk menghapus beberapa alamat sekaligus. Sekali lagi, kami menggambarkan algoritme:
  • Path yang dihasilkan ke file cache kita akan menulis ke file;
  • Mari kita menulis skrip bash sederhana yang kita pasang di mesin dengan aplikasi. Tugasnya adalah untuk terhubung melalui ssh ke server di mana kita memiliki caching nginx dan menghapus semua file cache yang ditentukan dalam file yang dihasilkan dari langkah 1;

Kami beralih dari teori ke praktek, kami akan menulis contoh kecil yang menggambarkan algoritma kerja kami.

Langkah 1. Membuat file dengan jalur ke cache.

 $urls = [ 'httpGETdomain.ru/news/111/1:2', 'httpGETdomain.ru/news/112/3:4', ]; function to_nginx_cache_path(url) { $nginxHash = md5($url); $firstDir = substr($nginxHash, -1, 1); $secondDir = substr($nginxHash, -3, 2); return "/var/lib/nginx/proxy_cache/$firstDir/$secondDir/$nginxHash"; } //        tmp $filePath = tempnam('tmp', 'nginx_cache_'); //      $fileStream = fopen($filePath, 'a'); foreach ($urls as $url) { //      $cachePath = to_nginx_cache_path($url); //       fwrite($fileStream, $cachePath . PHP_EOL); } //     fclose($fileStream); //  bash       exec("/usr/local/bin/cache_remover $filePath"); 

Harap dicatat bahwa variabel $ url berisi url dari halaman yang di-cache, sudah dalam format proxy_cache_key yang ditentukan dalam konfigurasi nginx. Url bertindak sebagai tag untuk entitas yang ditampilkan di halaman. Misalnya, Anda bisa membuat tabel biasa di dalam basis data, di mana setiap entitas akan dipetakan ke halaman tertentu di mana ia ditampilkan. Kemudian, ketika mengubah data apa pun, kita dapat membuat pilihan di atas meja dan menghapus cache dari semua halaman yang kita butuhkan.

Langkah 2. Hubungkan ke server cache dan hapus file cache.

 #      ,      FILE_LIST=`cat $1 | tr "\n" " "` #   ssh  SSH=`which ssh` USER="root" #         nginx HOST="10.10.1.0" #   KEY="/var/keys/id_rsa" # SSH ,          $SSH -i ${KEY} ${USER}@${HOST} "rm -f ${FILE_LIST}" #       rm -rf rm -f $1 #   

Contoh di atas hanya untuk panduan, jangan menggunakannya dalam produksi. Dalam contoh, pemeriksaan parameter input dan pembatasan perintah dihilangkan. Salah satu masalah yang mungkin Anda temui adalah membatasi panjang argumen ke perintah rm. Ketika menguji dalam lingkungan dev pada volume kecil, ini dapat dengan mudah dilewatkan, dan dalam produksi Anda mendapatkan kesalahan "rm: Daftar argumen terlalu panjang".

Caching Blok Khusus


Mari kita simpulkan apa yang berhasil kita lakukan:

  • mengurangi beban di backend;
  • Pelajari cara mengelola caching
  • belajar menyiram cache pada waktu tertentu.

Tapi tidak semuanya sebagus kelihatannya pada pandangan pertama. Sekarang, mungkin, jika tidak setiap pertama, maka tepatnya setiap situs kedua memiliki fungsi registrasi / otorisasi, setelah melewati mana kami ingin menampilkan nama pengguna di suatu tempat di header. Blokir dengan nama tersebut unik dan harus menampilkan nama pengguna yang kami otorisasi. Karena nginx menyimpan respons dari backend, dan dalam kasus halaman itu adalah konten html halaman, blok dengan data pribadi juga akan di-cache. Semua pengunjung ke situs akan melihat nama pengguna pertama yang diteruskan ke backend untuk satu set cache.
Oleh karena itu, backend tidak boleh memberikan blok di mana informasi pribadi berada sehingga informasi ini tidak termasuk dalam cache nginx.

Penting untuk mempertimbangkan pemuatan alternatif dari bagian halaman tersebut. Seperti biasa, ini dapat dilakukan dengan banyak cara, misalnya, setelah memuat halaman, mengirim permintaan ajax, dan menampilkan loader di tempat konten pribadi. Cara lain yang akan kita pertimbangkan hari ini adalah dengan menggunakan tag ssi. Pertama mari kita pahami apa itu SSI, dan kemudian bagaimana kita bisa menggunakannya bersamaan dengan cache nginx.

Apa itu SSI dan bagaimana cara kerjanya


SSI (Termasuk Sisi-Server, inklusi sisi-server) adalah sekumpulan perintah yang tertanam dalam laman html yang memberi tahu server apa yang harus dilakukan.

Berikut adalah daftar perintah (arahan) tersebut:

• if / elif / else / endif - Operator percabangan;
• echo - Menampilkan nilai variabel;
• include - Memungkinkan Anda untuk memasukkan konten file lain ke dalam dokumen.
Arahan terakhir akan dibahas. Arahan include memiliki dua parameter:
• file - Menentukan jalur ke file di server. Mengenai direktori saat ini;
• virtual - Menunjukkan jalur virtual ke dokumen di server.

Kami tertarik pada parameter "virtual", karena menentukan path lengkap ke file di server tidak selalu nyaman, atau dalam kasus arsitektur terdistribusi, file di server sama sekali tidak ada. Arahan contoh:

 <!--#include virtual="/user/personal_news/"--> 

Agar nginx mulai memproses sisipan ssi, Anda perlu mengubah lokasi sebagai berikut:

 location / { ssi on; ... } 

Sekarang semua permintaan yang diproses oleh lokasi "/" akan dapat melakukan sisipan ssi.

Bagaimana permintaan kami melalui seluruh skema ini?

  • klien meminta halaman;
  • Nginx proksi permintaan backend;
  • backend memberi halaman dengan sisipan ssi;
  • hasilnya disimpan dalam cache;
  • Nginx "bertanya" blok yang hilang;
  • Halaman yang dihasilkan dikirim ke klien.

Seperti yang Anda lihat dari langkah-langkahnya, konstruksi ssi akan masuk ke cache nginx, yang akan memungkinkan untuk tidak melakukan cache blok pribadi, dan halaman html siap pakai dengan semua sisipan akan dikirim ke klien. Di sini pemuatan kami berfungsi, nginx secara mandiri meminta blok halaman yang hilang. Tetapi seperti solusi lainnya, pendekatan ini memiliki pro dan kontra. Bayangkan bahwa ada beberapa blok pada halaman yang harus ditampilkan secara berbeda tergantung pada pengguna, maka setiap blok tersebut akan diganti dengan insert ssi. Nginx, seperti yang diharapkan, akan meminta setiap blok seperti itu dari backend, yaitu, satu permintaan dari pengguna akan segera menghasilkan beberapa permintaan untuk backend, yang saya tidak mau sama sekali.

Menyingkirkan permintaan backend persisten melalui ssi


Untuk mengatasi masalah ini, modul nginx “ngx_http_memcached_module” akan membantu kami. Modul ini memungkinkan penerimaan nilai dari server memcached. Menulis melalui modul tidak akan berfungsi, server aplikasi harus mengurus ini. Pertimbangkan contoh kecil konfigurasi nginx bersamaan dengan modul:

 server { location /page { set $memcached_key "$uri"; memcached_pass 127.0.0.1:11211; error_page 404 502 504 = @fallback; } location @fallback { proxy_pass http://backend; } } 

Dalam variabel $ memcache_key kami menentukan kunci yang nginx akan mencoba untuk mendapatkan data dari memcache. Parameter untuk menghubungkan ke server memcache diatur dalam direktif memcached_pass. Koneksi dapat ditentukan dalam beberapa cara:

• nama domain;

 memcached_pass cache.domain.ru; 

• Alamat dan port IP;

 memcached_pass localhost:11211; 

• soket unix;

 memcached_pass unix:/tmp/memcached.socket; 

• arahan hulu.

 upstream cachestream { hash $request_uri consistent; server 10.10.1.1:11211; server 10.10.1.2:11211; } location / { ... memcached_pass cachestream; ... } 

Jika nginx berhasil mendapatkan respons dari server cache, maka itu memberikannya kepada klien. Jika tidak ada data dalam cache, permintaan akan dikirim ke backend melalui "@fallback". Pengaturan kecil ini dari modul memcached di bawah nginx akan membantu kami mengurangi jumlah permintaan lewat untuk backend dari sisipan ssi.

Kami berharap artikel ini bermanfaat dan kami dapat menunjukkan salah satu cara untuk mengoptimalkan beban di server, mempertimbangkan prinsip dasar pengaturan caching nginx dan menutup masalah yang muncul saat menggunakannya.

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


All Articles