Biarkan pernis mengirim data lama dari cache saat mengambil yang baru?

8

Saya caching halaman yang dibuat secara dinamis (PHP-FPM, NGINX) dan ada pernis di depannya, ini bekerja dengan sangat baik.

Namun, setelah batas waktu cache tercapai, saya melihat ini:

  • halaman permintaan klien baru
  • varnish mengenali batas waktu cache
  • klien menunggu
  • pernis mengambil halaman baru dari backend
  • varnish mengirim halaman baru ke klien (dan memiliki halaman di-cache juga, untuk permintaan berikutnya yang langsung mendapatkannya)

Yang ingin saya lakukan adalah:

  • halaman permintaan klien
  • pernis mengakui batas waktu
  • pernis mengirimkan halaman lama ke klien
  • pernis mengambil halaman baru dari backend dan memasukkannya ke dalam cache

Dalam kasus saya, ini bukan situs di mana informasi yang ketinggalan jaman adalah masalah besar, terutama tidak ketika kita berbicara tentang cache timeout beberapa menit.

Namun, saya tidak ingin menghukum pengguna untuk menunggu dalam antrean dan alih-alih mengirimkan sesuatu dengan segera. Apakah itu mungkin?

Sebagai ilustrasi, berikut adalah contoh hasil dari pengepungan 5 menit terhadap server saya yang dikonfigurasi untuk cache selama satu menit:

HTTP/1.1,200,  1.97,  12710,/,1,2013-06-24 00:21:06
...
HTTP/1.1,200,  1.88,  12710,/,1,2013-06-24 00:21:20
...
HTTP/1.1,200,  1.93,  12710,/,1,2013-06-24 00:22:08
...
HTTP/1.1,200,  1.89,  12710,/,1,2013-06-24 00:22:22
...
HTTP/1.1,200,  1.94,  12710,/,1,2013-06-24 00:23:10
...
HTTP/1.1,200,  1.91,  12709,/,1,2013-06-24 00:23:23
...
HTTP/1.1,200,  1.93,  12710,/,1,2013-06-24 00:24:12
...

Saya mengabaikan ratusan permintaan yang berjalan sekitar 0.02. Tetapi saya masih khawatir bahwa akan ada pengguna yang harus menunggu hampir 2 detik untuk HTML mentah mereka.

Tidak bisakah kita melakukan yang lebih baik di sini?

(Saya menemukan Varnish kirim sementara cache , kedengarannya mirip tetapi tidak persis apa yang saya coba lakukan.)

Larutan

Jawaban dari Shane Madden berisi solusinya, tetapi saya tidak segera menyadarinya. Ada detail lain yang tidak saya sertakan dalam pertanyaan saya karena saya pikir itu tidak relevan, tetapi sebenarnya itu relevan.

Solusi CMS yang saya gunakan saat ini memiliki pendengar basis data pernis dan dengan demikian memiliki kemampuan untuk memberi tahu pernis untuk mencekal halaman yang isinya telah berubah. Itu mengirim PURGEpermintaan dengan beberapa regex untuk melarang halaman tertentu.

Singkatnya, ada dua kasus di mana saya mendapat pengguna yang tidak beruntung:

  1. TTL pernis normal halaman kedaluwarsa
  2. pengguna backend mengubah konten, ini mengirimkan permintaan pembersihan untuk pernis

Dalam kedua kasus saya memiliki pengguna "sial". Dalam kasus kedua ini diringankan oleh fakta bahwa pengguna backend biasanya memeriksa halaman setelah itu diubah; tapi belum tentu.

Namun demikian, untuk kasus kedua saya menciptakan solusi (ya, saya menyadari pertanyaan ini dimulai dengan mencari jawaban untuk kasus pertama ... pertanyaan yang dirumuskan dengan buruk di pihak saya):

Alih-alih mengirim permintaan pembersihan, saya menggunakan saran Shanes dan menyesuaikan VCL sehingga pendengar basis data pernis saya dapat mengirim permintaan khusus untuk mengambil halaman dengan hash_always_missset ke true.

Dengan arsitektur saat ini saya tidak benar-benar memiliki kemewahan melakukan permintaan asinkron nyata, tetapi dengan bantuan Bagaimana cara membuat permintaan GET asinkron dalam PHP? Saya dapat membuat permintaan GET ke pernis yang tidak menunggu halaman dimuat tetapi cukup baik untuk memicu pernis untuk mengambil halaman dari backend dan menyimpannya.

Efek bersihnya adalah bahwa pendengar basis data mengirim permintaan untuk pernis dan ketika saya sedang polling halaman tertentu itu tidak pernah membuat permintaan saya "sial" tetapi sekali pernis mengambil halaman sepenuhnya dari backend (ini mungkin berkisar dari 300ms ke 2s) itu tiba-tiba ada di sana.

Saya belum menemukan solusi bagaimana menghindari masalah yang sama ketika TTL normal habis, tapi saya kira solusinya juga persis seperti yang disarankan Shane: menggunakan wget untuk memicu hash_always_miss, saya hanya harus cukup pintar untuk mendapatkan daftar halaman saya harus menyegarkan.

menandai
sumber

Jawaban:

3

Solusi yang saya gunakan untuk menyelesaikan masalah ini adalah memastikan TTL pada suatu halaman tidak pernah memiliki kesempatan untuk kedaluwarsa sebelum disegarkan - memaksa klien HTTP yang menjalankan salah satu sistem saya untuk mendapatkan beban yang lambat alih-alih klien yang kurang beruntung permintaan.

Dalam kasus saya, ini melibatkan wgetcron, mengirimkan header khusus untuk menandai permintaan dan pengaturan req.hash_always_missberdasarkan ini, memaksa salinan baru dari konten yang akan diambil ke dalam cache.

acl purge {
    "localhost";
}

sub vcl_recv {
    /* other config here */
    if (req.http.X-Varnish-Nuke == "1" && client.ip ~ purge) {
        set req.hash_always_miss = true;
    }
    /* ... */
}

Untuk konten Anda, ini mungkin berarti menyetel Varnish TTL menjadi sekitar 5 menit tetapi memiliki cron'd wget yang dikonfigurasi untuk membuat permintaan penyegaran cache setiap menit.

Shane Madden
sumber
Saya pikir saya mengerti apa yang Anda maksud. Ini akan bekerja dengan baik untuk satu halaman, tetapi ribuan lainnya? Tidak dapat menggunakan cron / wget pada skala itu.
tandai
Secara intrinsik, Anda harus mendeklarasikan setidaknya halaman mana yang Anda inginkan tetap terkini dalam cache. Dengan daftar ini, tidak ada alasan wget dalam skrip cron tidak dapat membantu Anda.
Falcon Momot
Saya pikir itulah tantangan dengan "pendekatan eksternal pernis" ini. Saya akhirnya harus duduk dan mengambil keputusan serta mengimplementasikannya untuk setiap halaman. Tidak ada cara untuk mengatakan pernis: "ttl kehabisan? Ambil versi baru tapi sampai itu terjadi melayani yang lama" ...? Hmm.
tandai
4

@EDIT:

Hanya yang cepat untuk memberi tahu Anda bahwa fitur ini tampaknya baru saja diterapkan di versi terbaru di cabang utama, kemungkinan versi Anda mungkin belum mendukung basi-sementara-validasi lagi / contoh yang saya posting akan melayani 9999/10000 permintaan dengan satu bugger miskin masih harus menunggu permintaan selesai di backend (Masih lebih baik daripada tidak sama sekali;) ...


Yah saya tidak 100% yakin mengapa komentar sebelumnya mengatakan itu tidak berfungsi tetapi menurut: https://www.varnish-software.com/static/book/Saving_a_request.html

  • req.grace - mendefinisikan berapa lama objek bisa jatuh tempo bagi Varnish untuk tetap menganggapnya sebagai mode rahmat.
  • beresp.grace - mendefinisikan berapa lama melewati beresp.ttl-time Varnish akan menyimpan suatu objek.
  • req.grace - sering dimodifikasi dalam vcl_recv berdasarkan kondisi backend.

Saat ini saya menggunakan konfigurasi seperti yang dikatakan manual dan berfungsi dengan baik ... Berikut cuplikan file vcl saya ...

sub vcl_recv {
    # Cache rules above here...
    if (req.backend.healthy) {
        set req.grace = 30d;
    } else {
        set req.grace = 300d;
    }
}

sub vcl_fetch {
    # Fetch rules above here ...

    # If backend returns 500 error then boost the cache grace period...
    if (beresp.status == 500) {
        set beresp.grace = 10h;
        return (restart);
    }

    # How long carnish should keep the objects in cache..
    set beresp.grace = 1h;

    # Actual TTL of cache - If an object is older than this an update will be triggered to the backend server :)
    set beresp.ttl = 1m;
}

Perhatikan bahwa jika Anda ingin memberikan waktu tenggang waktu respons backend yang lebih lama (untuk 500 kesalahan seperti pada konfigurasi saya), Anda perlu mengatur backend probing ... Berikut adalah salinan probe backend saya ..

backend default {
    .host = "127.0.0.1";
    .port = "8080";
    .probe = { 
        .url = "/nginx-status";
        .timeout = 500 ms; 
        .interval = 3s; 
        .window = 10;
        .threshold = 4;
    }
}
David
sumber
apakah ini masih berlaku untuk vcl 4.0?
Gadelkareem
0

Dalam Varnish 3 ini dicapai melalui "mode Grace". Menurut manual [1], Anda harus menambahkan logika berikut:

sub vcl_fetch {
  set beresp.grace = 30m;
} // Makes objects to be cached/stored 30 min beyond its max-age/ttl

sub vcl_recv {
  set req.grace = 60s;
} // Allows varnish to serve objects which expired within last minute.

[1] https://www.varnish-cache.org/docs/3.0/tutorial/handling_misbehaving_servers.html#grace-mode

NITEMAN
sumber
Ini tidak akan mencapai tujuan OP, satu klien masih akan tertunda menunggu konten diambil dari backend, meskipun klien lain di belakang yang satu akan dilayani hasil rahmat basi. "Setelah TTL kedaluwarsa, klien pertama yang meminta konten harus macet selama 15 detik, sedangkan klien kedua harus mendapatkan salinan rahmat." - varnish-software.com/static/book/Saving_a_request.html
Pax
Maaf, saya tidak menyadari bagaimana cara kerjanya dengan permintaan kadaluarsa pertama.
NITEMAN
Mungkin tujuan yang diinginkan dapat dicapai dengan beberapa cheat ttl & inline C untuk memecat "permintaan latar belakang"
NITEMAN