Механизм кэширования в узле

Node.js сервер браузер

Кэширование — очень важная концепция при разработке узлов, оно используется во многих местах, например: в браузерах есть кэши, в DNS есть кэши, и на серверах тоже есть кэши.

1. Роль кэширования

Кэш для чего?

1. Для повышения скорости и повышения эффективности.

2. Уменьшите передачу данных и сократите плату за сеть.

3. Снизить нагрузку на сервер и повысить производительность сайта.

4. Ускорить загрузку веб-страниц на стороне клиента.

2. Классификация кеша

Сколько существует стратегий кэширования?

Принудительный кеш:

1. Концепция:

Клиент обращается к серверу, чтобы запросить ресурс. После успешного запроса клиент кэширует его локально. После локального сохранения кеша, если клиент запрашивает ресурс в будущем, ему не нужно запрашивать сервер в этот момент. время, и он может напрямую получить доступ к локальному.

2. Особенности:

Принудительное кэширование не требует взаимодействия с сервером

3. Схема клиентского доступа к принудительному кешу

1) попадание в кеш Клиент запрашивает данные, и теперь они просматриваются в базе данных локального кеша, если данные существуют в базе данных локального кеша и данные не являются недействительными. Затем возьмите данные из базы данных кеша и верните их клиенту.

2) промах кеша Клиент запрашивает данные, и теперь они просматриваются в базе данных локального кеша, если данные существуют в базе данных локального кеша, и данные недействительны. Затем запросите данные с сервера.В это время сервер возвращает данные и правила кэширования данных клиенту.После того, как клиент получает данные и правила кэширования, они вместе хранятся в базе данных локального кэша. для следующего использования.

4. Как реализовать принудительное кэширование?

1. Браузер кэширует файл в каталоге Cache. Браузер сначала проверит, содержит ли файл файл в каталоге Cache во время второго запроса. Если да, и он не достиг времени, установленного Expires, то есть, срок действия файла не истек, то это Когда браузер будет напрямую читать файл из каталога Cache и больше не будет отправлять запрос 2. Expires – это поле заголовка ответного сообщения сервера. При ответе на HTTP-запрос оно сообщает браузеру, что браузер может напрямую извлекать данные из кеша браузера до истечения срока действия без повторного запроса. Это содержимое HTTP1. .0. HTTP1.1 используется по умолчанию, поэтому его можно игнорировать. 3. Функции Cache-Control и Expires одинаковые, они обе указывают срок действия текущего ресурса и контролируют, извлекает ли браузер данные напрямую из кеша браузера или повторно отправляет запрос на сервер для выборки данных. устанавливается одновременно, его приоритет выше, чем Expires.

Кэшируйте ресурс на клиенте. Если клиенту снова понадобится ресурс, он сначала получит данные в кеше, чтобы узнать, не истек ли срок его действия. Запросить сервер снова Если он не истекает, вам вообще не нужно подтверждать сервер, просто используйте локальный кеш напрямую.

Кэш-Контроль частные клиенты могут кэшировать И общедоступные клиенты, и прокси-серверы могут кэшировать max-age=60 содержимое кеша истечет через 60 секунд no-cache Требует сравнительного кэша для проверки данных, принудительной повторной аутентификации на исходном сервере Отключить принудительное кэширование no-store Все содержимое не будет кэшироваться, и ни принудительное кэширование, ни контрастное кэширование не будут активированы. Используйте как принудительный кеш, так и контрастный кеш

/**
* 1. 第一次访问服务器的时候,服务器返回资源和缓存的标识,客户端则会把此资源缓存在本地的缓存数据库中。
* 2. 第二次客户端需要此数据的时候,要取得缓存的标识,然后去问一下服务器我的资源是否是最新的。
* 如果是最新的则直接使用缓存数据,如果不是最新的则服务器返回新的资源和缓存规则,客户端根据缓存规则缓存新的数据。
*/
let http = require('http');
let url = require('url');
let path = require('path');
let fs = require('fs');
let mime = require('mime');
let crypto = require('crypto');
/**
* 强制缓存
* 把资源缓存在客户端,如果客户端再次需要此资源的时候,先获取到缓存中的数据,看是否过期,如果过期了。再请求服务器
* 如果没过期,则根本不需要向服务器确认,直接使用本地缓存即可
*/
http.createServer(function (req, res) {
   let { pathname } = url.parse(req.url, true);
   let filepath = path.join(__dirname, pathname);
   console.log(filepath);
   fs.stat(filepath, (err, stat) => {
       if (err) {
           return sendError(req, res);
       } else {
           send(req, res, filepath);
}
});
}).listen(8080);
function sendError(req, res) {
   res.end('Not Found');
}
function send(req, res, filepath) {
   res.setHeader('Content-Type', mime.getType(filepath));
   //expires指定了此缓存的过期时间,此响应头是1.0定义的,在1.1里面已经不再使用了
   res.setHeader('Expires', new Date(Date.now() + 30 * 1000).toUTCString());
   res.setHeader('Cache-Control', 'max-age=30');
   fs.createReadStream(filepath).pipe(res);
}

Сравните кеш:

1. Концепция:

Когда браузер запрашивает данные в первый раз, сервер возвращает идентификатор кеша и данные клиенту, а клиент создает их резервную копию в базе данных кеша. При повторном запросе данных клиент отправляет резервный идентификатор кеша на сервер, и сервер выносит решение на основе идентификатора кеша. После успешного решения он возвращает код состояния 304, чтобы уведомить клиента об успешном сравнении. и кэшированные данные могут быть использованы.

2. Особенности: необходимо сравнить, чтобы определить, можно ли использовать кеш.
3. Сравнение диаграмм потоков кэша

1) Клиент отправляет запрос в первый раз

Когда клиент запрашивает данные в первый раз и обнаруживает, что в локальном кэше нет данных, он инициирует запрос к серверу, а затем сервер возвращает запрошенные данные клиенту и согласовывает с клиентом правила, которые вы хотите сохранить в локальный кеш, то есть кешировать кеш или нет.Время указывает время последней модификации и прочую информацию? 2) Клиент отправляет второй запрос
Клиент инициирует запрос --->Проверить, есть ли кеш в локальной базе данных кеша --->Нет--->Инициировать запрос к серверу--->Сервер возвращает 200 и содержимое ответа--->Отобразить

---> Проверить, есть ли кеш в базе данных локального кеша ---> Да ---> Срок действия кеша не истек (локальный) ---> Считать в кеше ---> Показать

---> Проверить, есть ли кеш в базе данных локального кеша ---> Да ---> Срок действия кеша истек (локальный) ---> Есть ли Etag и Last-Modified в локальном кеше --- > Да ---> Отправить соответствующие поля на сервер if-none-match и if-modified-since ---> Политика сервера. Если эти два поля совпадают с этими двумя полями на сервере ---> это означает, что данные не обновлены ---> верните 304 ---> сервер передает данные из своей кэш-библиотеки клиенту --- -> дисплей

---> Проверить, есть ли кеш в базе данных локального кеша ---> Да ---> Срок действия кеша истек (локальный) ---> Есть ли Etag и Last-Modified в локальном кеше --- > Да ---> Отправить соответствующие поля на сервер if-none-match и if-modified-since ---> Политика сервера. Если эти два поля не совпадают с этими двумя полями на сервере ---> данные были обновлены ---> вернуть 200 ---> повторно получить ---> отобразить

4. Как реализовать кеш сравнения?

/**

    1. При первом обращении к серверу сервер возвращает идентификатор ресурса и кеша, а клиент кэширует ресурс в локальной базе данных кеша.
    1. Когда клиенту нужны эти данные во второй раз, ему нужно получить идентификатор кеша, а затем спросить сервер, актуальны ли мои ресурсы.
  • Если он самый последний, кэшированные данные используются напрямую, если не самый последний, сервер возвращает новые ресурсы и правила кэширования, а клиент кэширует новые данные в соответствии с правилами кэширования. */ Мы судим, действительны ли данные в кеше, помечая поле Существует две формы этого знака:
Первый — это время последней модификации, Last-Modified.

1. Last-Modified: Сообщите клиенту время последней модификации этого ресурса при ответе. 2. If-Modified-Since: когда срок действия ресурса истекает (используя максимальный возраст, определенный Cache-Control), и обнаруживается, что ресурс имеет объявление Last-Modified, заголовок If-Modified-Since добавляется к повторный запрос к серверу. 3. После получения запроса сервер находит наличие заголовка If-Modified-Since и сравнивает его со временем последней модификации запрошенного ресурса. Если время последней модификации более новое, что указывает на то, что ресурс был изменен снова, он ответит на последнее содержимое ресурса и вернет код состояния 200; 4. Если время последней модификации совпадает с If-Modified-Since, что указывает на то, что ресурс не был изменен, ответ 304 указывает, что он не был обновлен, и браузеру предлагается продолжать использовать сохраненный файл кеша. .

let http = require('http');
let url = require('url');
let path = require('path');
let fs = require('fs');
let mime = require('mime');
// http://localhost:8080/index.html
http.createServer(function (req, res) {
    let {pathname} = url.parse(req.url);
    let filepath = path.join(__dirname,pathname);
    console.log(filepath);
    fs.stat(filepath,function (err, stat) {
          if (err) {
              return sendError(req,res)
          } else {
              // 再次请求的时候会问服务器自从上次修改之后有没有改过
              let ifModifiedSince = req.headers['if-modified-since'];
              console.log(req.headers);
              let LastModified = stat.ctime.toGMTString();
              console.log(LastModified);
              if (ifModifiedSince == LastModified) {
                  res.writeHead('304');
                  res.end('')
              } else {
                  return send(req,res,filepath,stat)
              }
          }
    })

}).listen(8080)

function send(req,res,filepath,stat) {
    res.setHeader('Content-Type', mime.getType(filepath));
    // 发给客户端之后,客户端会把此时间保存下来,下次再获取此资源的时候会把这个时间再发给服务器
    res.setHeader('Last-Modified', stat.ctime.toGMTString());
    fs.createReadStream(filepath).pipe(res)
}

function sendError(req,res) {
    res.end('Not Found')
}
Проблема со временем последнего изменения

1. Некоторые серверы не могут точно получить время последней модификации файла, поэтому невозможно судить, обновлялся ли файл по времени последней модификации. 2. Некоторые файлы изменяются очень часто и изменяются в течение нескольких секунд.Последнее изменение может быть точным только до секунд. 3. Изменилось время последней модификации некоторых файлов, но содержимое не изменилось. Мы не хотим, чтобы клиенты думали, что этот файл был изменен. 4. Если один и тот же файл находится на нескольких серверах CDN, хотя содержимое одинаковое, время модификации разное.

Второй - Этаг.

ETag – это сокращение от тега объекта. Хэш-строка, сгенерированная в соответствии с содержимым объекта, может идентифицировать состояние ресурса. При изменении ресурса меняется и ETag. ETag генерируется веб-сервером и затем отправляется клиенту браузера.

1. Если клиент хочет определить, доступен ли кеш, он может сначала получить ETag документа в кеше, а затем отправить запрос на веб-сервер через If-None-Match, чтобы узнать, доступен ли кеш. 2. Сервер получает запрос и сравнивает ETag файла на сервере с If-None-Match в заголовке запроса, если значение одинаковое, значит кэш все еще актуален, а веб-сервер отправит код ответа 304 Not Modified. Указывает клиенту, что кэш не был изменен и может быть использован. 3. Если он отличается, веб-сервер отправит последнюю версию документа клиенту браузера.

let http = require('http');
let url = require('url');
let path = require('path');
let fs = require('fs');
let mime = require('mime');
let crypto = require('let crypto = require(\'mime\');\n');
// http://localhost:8080/index.html
http.createServer(function (req, res) {
    let {pathname} = url.parse(req.url);
    let filepath = path.join(__dirname,pathname);
    console.log(filepath);
    fs.stat(filepath,function (err, stat) {
        if (err) {
            return sendError(req,res)
        } else {

            let ifNoneMatch = req.headers['if-none-match'];
            // 一、显然当我们的文件非常大的时候通过下面的方法就行不通来,这时候我们可以用流来解决,可以节约内存
            let out = fs.createReadStream(filepath);
            let md5 = crypto.createHash('md5');
            out.on('data',function (data) {
                md5.update(data)
            });
            out.on('end',function () {
                let etag = md5.update(content).digest('hex');
                // md5算法的特点 1. 相同的输入相同的输出 2.不同的输入不通的输出 3.不能根据输出反推输入 4.任意的输入长度输出长度是相同的
                if (ifNoneMatch == etag) {
                    res.writeHead('304');
                    res.end('')
                } else {
                    return send(req,res,filepath,stat, etag)
                }
            });
            
            // 二、再次请求的时候会问服务器自从上次修改之后有没有改过
            // fs.readFile(filepath,function (err, content) {
            //     let etag = crypto.createHash('md5').update(content).digest('hex');
            //     // md5算法的特点 1. 相同的输入相同的输出 2.不同的输入不通的输出 3.不能根据输出反推输入 4.任意的输入长度输出长度是相同的
            //     if (ifNoneMatch == etag) {
            //         res.writeHead('304');
            //         res.end('')
            //     } else {
            //         return send(req,res,filepath,stat, etag)
            //     }
            // };
            // 但是上面的一方案也不是太好,读一点缓存一点,文件非常大的话需要好长时间,而且我们的node不适合cup密集型,即不适合来做大量的运算,所以说还有好多其他的算法
            // 三、通过文件的修改时间减去文件的大小
            // let etag = `${stat.ctime}-${stat.size}`; // 这个也不是太好
            // if (ifNoneMatch == etag) {
            //     res.writeHead('304');
            //     res.end('')
            // } else {
            //     return send(req,res,filepath,stat, etag)
            // }
        }
    })

}).listen(8080)

function send(req,res,filepath,stat, etag) {
    res.setHeader('Content-Type', mime.getType(filepath));
    // 第一次服务器返回的时候,会把文件的内容算出来一个标示发送给客户端
    //客户端看到etag之后,也会把此标识符保存在客户端,下次再访问服务器的时候,发给服务器
    res.setHeader('Etag', etag);
    fs.createReadStream(filepath).pipe(res)
}

function sendError(req,res) {
    res.end('Not Found')
}
Существует проблема

Всем нужно отправлять запросы на серверную часть для взаимодействия с серверной стороной