Pomijanie pamięci podręcznej za pomocą parametrów

123

Chcemy buforować błędy we wdrożeniach produkcyjnych, ale nie tracić czasu na wymyślanie odpowiedniego systemu. Pomyślałem o zastosowaniu parametru na końcu plików css i js o aktualnym numerze wersji:

<link rel="stylesheet" href="base_url.com/file.css?v=1.123"/>

Dwa pytania: czy to skutecznie zepsuje pamięć podręczną? Czy parametr spowoduje, że przeglądarka nigdy nie będzie buforować odpowiedzi z tego adresu URL, ponieważ parametr wskazuje, że jest to zawartość dynamiczna?

Brad Herman
źródło

Odpowiedzi:

115

Param ?v=1.123wskazuje ciąg kwerendy, a przeglądarka będzie więc myślę, że jest to nowa droga od, powiedzmy ?v=1.0. W ten sposób powoduje, że ładuje się z pliku, a nie z pamięci podręcznej. Jak chcesz.

I przeglądarka założy, że źródło pozostanie niezmienione następnym razem, gdy zadzwonisz ?v=1.123i powinna buforować je tym ciągiem. Więc pozostanie w pamięci podręcznej, jednak twój serwer jest skonfigurowany, dopóki nie przejdziesz do ?v=1.124lub tak dalej.

Marshall
źródło
4
Cytując Steve'a Soudersa: „Aby skorzystać z zalet buforowania przez popularne serwery proxy, unikaj revvingu z querystringiem i zamiast tego zmieniaj samą nazwę pliku”. Pełne wyjaśnienie można znaleźć tutaj: stevesouders.com/blog/2008/08/23/…
lao
25
Ten post na blogu ma już prawie dekadę. Czy uważasz, że dostawcy pamięci podręcznej i sieci CDN jeszcze go nie dostosowali? Squid wydaje się być w stanie buforować dokumenty z ciągów zapytań teraz .
jeteon
1
Może to komuś pomoże: Osobiście używam znacznika czasu modyfikacji pliku jako parametru wersji „automatycznej”, np. <link rel="stylesheet" href="style.css?v=1487935578" />
oelna
Osobiście nie rozumiem, dlaczego, ale Lara Hogan (Swanson) (kierownik ds. Inżynierii w Etsy) nie zaleca używania parametrów zapytania do pomijania pamięci podręcznej. Myślę, że ma to związek z serwerami proxy pamięci podręcznej między użytkownikiem a serwerem.
Sam Rueby
36

Dwa pytania: czy to skutecznie zepsuje pamięć podręczną?

Tak. Nawet Stack Overflow używają tej metody, chociaż pamiętam, że oni (z milionami odwiedzających dziennie i milionami różnych wersji i konfiguracji klienta i proxy) mieli kilka dziwacznych przypadków, w których nawet to nie wystarczyło, aby złamać pamięć podręczną. Ale ogólne założenie jest takie, że to zadziała i jest odpowiednią metodą do przerwania buforowania na klientach.

Czy parametr spowoduje, że przeglądarka nigdy nie będzie buforować odpowiedzi z tego adresu URL, ponieważ parametr wskazuje, że jest to zawartość dynamiczna?

Nie. Parametr nie zmieni zasad buforowania; nagłówki pamięci podręcznej wysłane przez serwer nadal obowiązują, a jeśli nie wysyła żadnych, ustawienia domyślne przeglądarki.

Pekka
źródło
1
@spender Nie mogę teraz znaleźć odniesienia Obawiam się, że był długi artykuł na blogu lub odpowiedź SO, w której mówi o tym Jeff Atwood (IIRC)
Pekka
2
@spender Czytałem, że niektóre serwery proxy (stare lub można je tak skonfigurować) ignorują ciąg zapytania podczas buforowania.
MrWhite
2
@spender - słyszałem to samo i myślę, że zmiana nazwy pliku lub ścieżki jest najlepszą opcją. Najłatwiej byłoby po prostu przenieść wszystkie swoje pliki statyczne pod wersjonowaną nazwą folderu, na przykład /static/v22/file.css, ponieważ możesz zrobić wiele plików z jedną zmianą nazwy folderu, np. /static/v23/file.cssI/static/v23/mystuff.js
Brad Parks
22

Bezpieczniej jest umieścić numer wersji w rzeczywistej nazwie pliku. Umożliwia to jednoczesne istnienie wielu wersji, dzięki czemu można wdrożyć nową wersję, a jeśli w pamięci podręcznej nadal istnieją strony HTML, które żądają starszej wersji, otrzymają wersję, która działa z ich kodem HTML.

Uwaga: w jednym z największych wdrożeń wersjonowanych w dowolnym miejscu w Internecie jQuery używa numerów wersji w rzeczywistej nazwie pliku i bezpiecznie umożliwia współistnienie wielu wersji bez specjalnej logiki po stronie serwera (każda wersja jest po prostu innym plikiem).

Spowoduje to jednorazowe zerwanie pamięci podręcznej podczas wdrażania nowych stron i nowych połączonych plików (co jest tym, czego chcesz) i od tego momentu te wersje mogą być skutecznie buforowane (co również chcesz).

jfriend00
źródło
Zgadzam się z tym, ale znacznie łatwiej jest po prostu dołączyć Sinatrę? V = <% = VERSION%> do wszystkich żądań css i js, zamiast kontrolować każdy plik z osobna. Ostatecznie przejdziemy na sinatra-assetpack, który wstępnie przetworzy i skompresuje wszystkie pliki oraz faktycznie doda numer wersji do nazwy pliku, co pozwoli nam znacznie łatwiej kontrolować je indywidualnie.
Brad Herman
1
Zgadzam się, że umieszczenie numeru wersji w nazwie pliku jest najbezpieczniejszym rozwiązaniem, jeśli chcesz mieć pewność na poziomie 10000%, ale nie podążam za argumentem „wiele wersji istnieje na raz”. Adres URL z parametrem zapytania różni się od tego samego adresu URL z innym parametrem zapytania. Klient powinien traktować je jako dwa różne zasoby; jeśli tak nie jest, klient jest zepsuty.
Pekka
2
@Pekka - numer wersji może pozwolić na istnienie wielu wersji jednocześnie, ale to wymaga współpracy serwera w celu zmapowania parametru zapytania na właściwy plik. Nie sądzę, aby to właśnie robił OP i nie ma powodu, aby wymagać, aby komplikacja podczas modyfikowania nazwy pliku była znacznie prostsza i nie wymagała współpracy serwera. Oczywiście oba mogą działać.
jfriend00
11

Jak powiedzieli inni, niszczenie pamięci podręcznej za pomocą parametru zapytania jest zwykle uważane za zły pomysł (tm) i było tak od dawna. Lepiej jest odzwierciedlić wersję w nazwie pliku. Html5 Gotowa odradza użyciu ciąg kwerendy, między innymi.

To powiedziawszy, spośród zaleceń, które widziałem, a które przytoczyły źródło, wszystkie wydają się czerpać swoją mądrość z artykułu Steve'a Soudersa z 2008 roku . Jego wnioski opierają się na zachowaniu serwerów proxy w tamtym czasie i obecnie mogą, ale nie muszą, mieć znaczenie. Mimo to, w przypadku braku bardziej aktualnych informacji, bezpieczną opcją jest zmiana nazwy pliku.

hashchange
źródło
9

Spowoduje to jednorazowe zniszczenie pamięci podręcznej, po pobraniu zasobu przez klienta każda inna odpowiedź będzie obsługiwana z pamięci podręcznej klienta, chyba że:

  1. parametr v jest aktualizowany.
  2. klient czyści pamięć podręczną
ncremins
źródło
6

Ogólnie powinno to wystarczyć, ale możliwe jest, że nie zadziała, jeśli istnieje pośrednia pamięć podręczna (proxy), która jest skonfigurowana tak, aby ignorować parametry żądania.

Na przykład, jeśli udostępniasz statyczną zawartość przez Akamai CDN, można ją skonfigurować tak, aby ignorowała parametry żądania, aby zapobiec pomijaniu pamięci podręcznej przy użyciu tej metody.

Ken Liu
źródło
5

W dużej mierze zależy to od tego, jak solidne ma być buforowanie. Na przykład serwer proxy squid (i być może inne) domyślnie nie buforuje adresów URL obsługiwanych z zapytaniem - przynajmniej tak było, gdy pisano ten artykuł. Jeśli nie masz nic przeciwko niektórym przypadkom użycia powodującym niepotrzebne chybienia w pamięci podręcznej, przejdź dalej z parametrami zapytania. Ale bardzo łatwo jest skonfigurować schemat niszczenia pamięci podręcznej oparty na nazwie pliku, który pozwala uniknąć tego problemu.

Bobby Jack
źródło
5
Serwer proxy squid, który był cytowany w artykule Steve'a Soudersa, zmienił swoją domyślną politykę buforowania. Od wersji 2.7 (maj 2008) i 3.1 (marzec 2010) domyślnym zachowaniem jest buforowanie zawartości dynamicznej.
Josh Rack
5

Znalazłem porównanie dwóch technik (ciąg zapytania vs nazwa pliku) tutaj :

Wersja jako querystring ma dwa problemy.

Po pierwsze, nie zawsze może to być przeglądarka, która implementuje buforowanie, przez które musimy się zepsuć. Mówi się, że niektóre (prawdopodobnie starsze) serwery proxy ignorują kwerendę dotyczącą ich zachowania w pamięci podręcznej.

Po drugie, w niektórych bardziej złożonych scenariuszach wdrażania, w których masz wiele serwerów frontendowych i / lub wielu serwerów zaplecza, aktualizacja nie jest natychmiastowa. Musisz być w stanie obsługiwać jednocześnie starą i nową wersję swoich zasobów. Zobacz na przykład, jak to wpływa na Ciebie podczas korzystania z Google App Engine.

użytkownik
źródło
4

Innym podobnym podejściem jest użycie htaccess mod_rewrite w celu zignorowania części ścieżki podczas udostępniania plików. Twoja nigdy nie buforowana strona indeksu odwołuje się do najnowszej ścieżki do plików.

Z punktu widzenia programowania jest to tak proste, jak użycie parametrów dla numeru wersji, ale jest tak solidne, jak podejście do nazwy pliku.

Użyj zignorowanej części ścieżki dla numeru wersji, a serwer po prostu go zignoruje i wyświetli plik niebuforowany.

1.2.3/css/styles.cssobsługuje ten sam plik, css/styles.cssponieważ pierwszy katalog jest usuwany i ignorowany przez plik htaccess

W tym wersjonowane pliki

<?php
  $version = "1.2.3";
?>

<html>
  <head>
    <meta http-equiv="cache-control" content="max-age=0" />
    <meta http-equiv="cache-control" content="no-cache" />
    <meta http-equiv="expires" content="0" />
    <meta http-equiv="expires" content="Tue, 01 Jan 1980 1:00:00 GMT" />
    <meta http-equiv="pragma" content="no-cache" />
    <link rel="stylesheet" type="text/css" href="<?php echo $version ?>/css/styles.css">
  </head>
  <body>
    <script src="<?php echo $version ?>/js/main.js"></script>
  </body>
</html>

Zauważ, że to podejście oznacza, że ​​musisz wyłączyć buforowanie swojej strony indeksu - używając tagów <meta>, aby wyłączyć buforowanie we wszystkich przeglądarkach?

Plik .htaccess

RewriteEngine On

# if you're requesting a file that exists, do nothing
RewriteCond %{REQUEST_FILENAME} !-f 
# likewise if a directory that exists, do nothing
RewriteCond %{REQUEST_FILENAME} !-d 

# otherwise, rewrite foo/bar/baz to bar/baz - ignore the first directory
RewriteRule ^[^/]+/(.+)$ $1 [L] 

Możesz zastosować to samo podejście na dowolnej platformie serwerowej, która umożliwia przepisywanie adresów URL

(warunek przepisania dostosowany z mod_rewrite - przepisanie katalogu do ciągu zapytania z wyjątkiem / #! / )

... a jeśli potrzebujesz pomijania pamięci podręcznej dla strony indeksu / punktu wejścia witryny, zawsze możesz użyć JavaSript, aby ją odświeżyć.

alexanderbird
źródło
2
<script type="text/javascript">
// front end cache bust

var cacheBust = ['js/StrUtil.js', 'js/protos.common.js', 'js/conf.js', 'bootstrap_ECP/js/init.js'];   
for (i=0; i < cacheBust.length; i++){
     var el = document.createElement('script');
     el.src = cacheBust[i]+"?v=" + Math.random();
     document.getElementsByTagName('head')[0].appendChild(el);
}
</script> 
Conete Cristian
źródło
Podczas opracowywania / testowania nowych wersji pamięć podręczna może stanowić problem, ponieważ przeglądarka, serwer, a nawet czasami telco 3G (jeśli wykonujesz wdrożenie mobilne) buforują statyczną zawartość (np. JS, CSS, HTML, img). Możesz temu zaradzić, dołączając numer wersji, losową liczbę lub sygnaturę czasową do adresu URL, np .: JSP: <script src = "js / excel.js? Time = <% = new java.util.Date ()%>"> </ skrypt> Jeśli używasz czystego HTML (zamiast stron serwera JSP, ASP, PHP) serwer Ci nie pomoże. W przeglądarce linki są ładowane przed uruchomieniem JS, dlatego należy je usunąć i załadować za pomocą JS
Conete Cristian
Nie sądzę, aby to ładowało pliki JS w kolejności, synchronicznie.
Stealth Rabbi
0
 <script>
    var storedSrcElements = [
         "js/exampleFile.js",
         "js/sampleFile.js",
         "css/style.css"
          ];

    var head= document.getElementsByTagName('head')[0];
    var script;
    var link;
    var versionNumberNew = 4.6;

    for(i=0;i<storedSrcElements.length;i++){
     script= document.createElement('script');
     script.type= 'text/javascript';
     script.src= storedSrcElements[i] + "?" + versionNumberNew;
     head.appendChild(script);
    }     


     </script> 


       ### Change the version number  (versionNumberNew) when you want the new files to be loaded  ###
Teja
źródło
0

Mam nadzieję, że to powinno pomóc ci wstrzyknąć zewnętrzny plik JS

<script type="text/javascript"> 
var cachebuster = Math.round(new Date().getTime() / 1000); 
document.write('<scr'+'ipt type="text/javascript" src="external.js?cb=' +cachebuster+'"></scr' + 'ipt>');
</script>

Źródło - kod Cachebuster w JavaScript

Vinit Kadkol
źródło