ホームページ  >  記事  >  ウェブフロントエンド  >  ノードを使用して http キャッシュの内容を解釈する

ノードを使用して http キャッシュの内容を解釈する

不言
不言転載
2018-10-29 14:48:251705ブラウズ

この記事は、http キャッシュの内容を解釈するためのノードの使用に関するものです。必要な方は参考にしていただければ幸いです。

ノードを使用して Web サービスを提供することは、Tomcat または Apache をサーバーとして直接使用することと同じではありません。 多くの作業は自分で行う必要があります。独自のキャッシュ戦略を選択する必要もありますが、静的リソースの管理に使用できる koa-static や Express.static などがありますが、開発または構成をより快適に行うには、http キャッシュについて理解する必要があります。さらに、フロントエンド最適化の重要なポイントとしての http キャッシュについても理解する必要があります。

http キャッシュとは

RFC 7234 (https://tools.ietf.org/pdf/rfc7234.pdf) では、HTTP キャッシュは応答メッセージのローカル ストレージであり、メッセージのストレージを制御すると述べています。取得および削除サブシステム。

平たく言うと、http プロトコルはいくつかの命令を規定しています。http プロトコルを実装するサーバーとブラウザは、これらの命令に基づいて、後で使用するために応答を保存するかどうか、およびその方法を決定します。

http キャッシュ 意味

応答速度の向上

帯域幅の使用量を削減し、トラフィックを節約します

サーバーの負荷を軽減します

キャッシュ関連の命令を指定しないでください

この場合、ブラウザはキャッシュせず、毎回サーバーにリクエストします。しかし、奇妙なのは、この場合、プロキシ サーバーが同じリクエストを複数回キャッシュすることです。

強制キャッシュ

いわゆる強制キャッシュでは、この時点でリソースの有効期限が満了するか、有効期限が切れます。リソースは制限時間内にキャッシュされる必要があります。

リソースを強力にキャッシュする方法

1.expires

このフィールドは、リソースの有効期限を定義します。実際の例:

ノードを使用して http キャッシュの内容を解釈する

expiresGMT 時間であることと、その動作メカニズムがわかります。最初のリクエストを行うときに、サーバーはリソースの有効期限を識別するために応答に expires を追加します。ブラウザはこのリソースをキャッシュし、再度リクエストするときに、最後のリクエストの有効期限を比較します。要求されたリソースとそれ自体のシステム時間を比較します。システム時間が有効期限よりも短い場合は、最後にキャッシュされたリソースが要求せずに直接使用されます。サーバーは応答で新しい有効期限を返します。

const d  = new Date(Date.now() + 5000);
res.writeHead(200, {
    'Content-Type': 'image/png',
    'expires': d.toGMTString()
});
res.end(img);

2.Cache-Control:[public | private,] max-age=${n}, s-maxage=${m}

expires 問題は、クライアントのシステム時刻に依存するため、HTTP1.1 で追加された Cache-Control# により、クライアントのシステム時刻エラーが発生する可能性があります。 ## この問題を解決するためのこのコマンドの値は比較的豊富で、一般的なものは次のとおりです:

  • public/private: 識別リソースをプロキシ サーバーによってキャッシュできるかどうか。

    public 識別リソースはプロキシ サーバーとブラウザの両方でキャッシュできます。 private 識別リソースはプロキシ サーバーでのみキャッシュできますが、ブラウザではキャッシュできません。

  • max-age: クライアント キャッシュの有効期間 (単位 s) を指定するために使用されます。n 秒を超える場合は、新しいリクエストが必要です。いいえ。それを超える場合は、キャッシュを使用できます

  • s-maxage: これはプロキシ サーバー用です。つまり、キャッシュ時間が制限されている場合、ソース サーバーからリソースを要求する必要はありません。

  • no-cache: このディレクティブは、ブラウザのキャッシュが使用されないことを意味します。また、ネゴシエートされたキャッシュを使用することもできます。

  • no-store: キャッシュがネゴシエートされても、応答に

    Last-Modified が含まれていても、ページには ## が含まれません。 #If-Modified-Since# ブラウザがリクエストしたとき

  • ##例

##ネゴシエーション キャッシュノードを使用して http キャッシュの内容を解釈するクライアントが使用したいのは、いわゆるネゴシエーション キャッシュです。リソースをキャッシュする場合、まずサーバーがリソースの有効期限が切れておらず、引き続き使用できるかどうかを確認します。使用されている場合は 304 応答が返され、クライアントは元のリソースを引き続き使用します。それ以外の場合は 200 が返され、そのリソースが応答本文に追加され、クライアントは新しいリソースを使用します。 1.Last-Modified と If-Modified-since

このメカニズムでは、サーバーが

Last-Modified

を応答ヘッダーに追加します。通常は、ブラウザーが取得するリソースの最終変更時刻です。この時刻は、最初のリクエストを行うときに、次のリクエストのリクエスト ヘッダーの

If-Modified-Since

に配置されます。サーバーは、この

If-Modified -Query を最終変更時刻として受け取ります。リソースのsince

time

nmと比較して、m>nの場合、200の応答を返し、updateLast-Modified は新しい値であり、リソースは本文内にあります。それ以外の場合、ブラウザは 304 応答が返され、最後にキャッシュされたリソースが使用されます。 2.Etag と If-None-Match

Last-Modified模式存两个问题, 一是它是秒级别的比对, 所以当资源的变化小于一秒时浏览器可能使用错误的资源; 二是资源的最新修改时间变了可能内容并没有变, 但是还是会给出完整响应, 造成浪费. 基于此在HTTP1.1引入了Etag模式.

这个与上面的Last-Modified机制基本相同, 不过不再是比对最后修改时间而是比对资源的标识, 这个Etag一般是基于资源内容生成的标识. 由于Etag是基于内容生成的, 所以当且仅当内容变化才会给出完整响应, 无浪费和错误的问题.

演示第8, 10

如何选择缓存策略

https://tools.ietf.org/pdf/rfc7234.pdf

附录

1.演示代码

const http = require('http');
const fs = require('fs');
let etag = 0;
let tpl = fs.readFileSync('./index.html');
let img = fs.readFileSync('./test.png');
http.createServer((req, res) => {
    etag++; // 我是个假的eTag
    console.log('--->', req.url);
    switch (req.url) {
        // 模板
        case '/index':
            res.writeHead(200, {
                'Content-Type': 'text/html',
                'Cache-Control': 'no-store'
            });
            res.end(tpl);
            break;
        // 1. 不给任何与缓存相关的头, 任何情况下, 既不会被浏览器缓存, 也不会被代理服务缓存
        case '/img/nothing_1':
            res.writeHead(200, {
                'Content-Type': 'image/png'
            });
            res.end(img);
            break;
            
        // 2. 设置了no-cache表明每次要使用缓存资源前需要向服务器确认
        case '/img/cache-control=no-cache_2':
            res.writeHead(200, {
                'Content-Type': 'image/png',
                'cache-control': 'no-cache'
            });
            res.end(img);
            break;

        // 3. 设置max-age表示在浏览器最多缓存的时间
        case '/img/cache-control=max-age_3':
            res.writeHead(200, {
                'Content-Type': 'image/png',
                'cache-control': 'max-age=10'
            });
            res.end(img);
            break;

        // 4. 设置了max-age s-maxage public: public 是说这个资源可以被服务器缓存, 也可以被浏览器缓存, 
        // max-age意思是浏览器的最长缓存时间为n秒, s-maxage表明代理服务器的最长缓存时间为那么多秒
        case '/img/cache-control=max-age_s-maxage_public_4':
            res.writeHead(200, {
                'Content-Type': 'image/png',
                'cache-control': 'public, max-age=10, s-maxage=40'
            });
            res.end(img);
            break;

        // 设置了max-age s-maxage private: private 是说这个资源只能被浏览器缓存, 不能被代理服务器缓存
        // max-age说明了在浏览器最长缓存时间, 这里的s-maxage实际是无效的, 因为不能被代理服务缓存
        case '/img/cache-control=max-age_s-maxage_private_5':
            res.writeHead(200, {
                'Content-Type': 'image/png',
                'cache-control': 'private, max-age=10, s-maxage=40'
            });
            res.end(img);
            break;
        
        // 7. 可以被代理服务器缓存, 确不能被浏览器缓存
        case '/img/cache-control=private_max-age_7':
            res.writeHead(200, {
                'Content-Type': 'image/png',
                'cache-control': 'public, s-maxage=40'
            });
            res.end(img);
            break;
        // 8. 协商缓存
        case '/img/talk_8':
            let stats = fs.statSync('./test.png');
            let mtimeMs = stats.mtimeMs;
            let If_Modified_Since = req.headers['if-modified-since'];
            let oldTime = 0;
            if(If_Modified_Since) {
                const If_Modified_Since_Date = new Date(If_Modified_Since);
                oldTime = If_Modified_Since_Date.getTime();
            }
            
            mtimeMs = Math.floor(mtimeMs / 1000) * 1000;    // 这种方式的精度是秒, 所以毫秒的部分忽略掉
            console.log('mtimeMs', mtimeMs);
            console.log('oldTime', oldTime);
            if(oldTime <p>2.测试用代理服务器nginx配置</p><p>不要问我这是个啥, 我是copy的</p><pre class="brush:php;toolbar:false">worker_processes  8;
  
events {
    worker_connections  65535;
}
  
http {
    include       mime.types;
    default_type  application/octet-stream;
    charset utf-8;
 
    log_format  main  '$http_x_forwarded_for $remote_addr $remote_user [$time_local] "$request" '
                      '$status $body_bytes_sent "$http_referer" '
                      '"$http_user_agent" "$http_cookie" $host $request_time';
    sendfile       on;
    tcp_nopush     on;
    tcp_nodelay    on;
    keepalive_timeout  65;
    proxy_connect_timeout 500;
    #跟后端服务器连接的超时时间_发起握手等候响应超时时间
    proxy_read_timeout 600;
    #连接成功后_等候后端服务器响应的时间_其实已经进入后端的排队之中等候处理
    proxy_send_timeout 500;
    #后端服务器数据回传时间_就是在规定时间内后端服务器必须传完所有数据
    proxy_buffer_size 128k;
    #代理请求缓存区_这个缓存区间会保存用户的头信息以供Nginx进行规则处理_一般只要能保存下头信息即可  
    proxy_buffers 4 128k;
    #同上 告诉Nginx保存单个用的几个Buffer最大用多大空间
    proxy_busy_buffers_size 256k;
    #如果系统很忙的时候可以申请更大的proxy_buffers 官方推荐*2
    proxy_temp_file_write_size 128k;
    #设置web缓存区名为cache_one,内存缓存空间大小为12000M,自动清除超过15天没有被访问过的缓存数据,硬盘缓存空间大小200g
    #要想开启nginx的缓存功能,需要添加此处的两行内容!
    #设置Web缓存区名称为cache_one,内存缓存空间大小为500M,缓存的数据超过1天没有被访问就自动清除;访问的缓存数据,硬盘缓存空间大小为30G
    proxy_cache_path /usr/local/nginx/proxy_cache_path levels=1:2 keys_zone=cache_one:500m inactive=1d max_size=30g;
 
    #创建缓存的时候可能生成一些临时文件存放的位置
    proxy_temp_path /usr/local/nginx/proxy_temp_path;
 
    fastcgi_connect_timeout 3000;
    fastcgi_send_timeout 3000;
    fastcgi_read_timeout 3000;
    fastcgi_buffer_size 256k;
    fastcgi_buffers 8 256k;
    fastcgi_busy_buffers_size 256k;
    fastcgi_temp_file_write_size 256k;
    fastcgi_intercept_errors on;
  
     
    client_header_timeout 600s;
    client_body_timeout 600s;
  
    client_max_body_size 100m;             
    client_body_buffer_size 256k;           
  
    gzip  off;
    gzip_min_length  1k;
    gzip_buffers     4 16k;
    gzip_http_version 1.1;
    gzip_comp_level 9;
    gzip_types       text/plain application/x-javascript text/css application/xml text/javascript;
    gzip_vary on;
  
 
    include vhosts/*.conf;
    server {
        listen       80;
        server_name  localhost;
        location / {
            proxy_pass  http://127.0.0.1:1234;
            proxy_set_header   Host             $http_host;
            proxy_set_header   X-Real-IP        $remote_addr;
            proxy_set_header   X-Forwarded-For  $proxy_add_x_forwarded_for;
            proxy_redirect off;
            proxy_cache cache_one;
            #此处的cache_one必须于上一步配置的缓存区域名称相同
            proxy_cache_valid 200 304 12h;
            proxy_cache_valid 301 302 1d;
            proxy_cache_valid any 1h;
            #不同的请求设置不同的缓存时效
            proxy_cache_key $uri$is_args$args;
            #生产缓存文件的key,通过4个string变量结合生成
            expires off;
            #加了这个的话会自己修改cache-control, 写成off则不会
            proxy_set_header X-Forwarded-Proto $scheme;
        }
    }
}

以上がノードを使用して http キャッシュの内容を解釈するの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事はsegmentfault.comで複製されています。侵害がある場合は、admin@php.cn までご連絡ください。