Эти вещи о кэшировании HTTP

Node.js внешний интерфейс сервер браузер

HTTP 缓存策略


читать оригинал


предисловие

Являясь важным средством оптимизации производительности веб-приложений, механизм HTTP-кэширования должен быть основным звеном системы знаний для студентов, занимающихся веб-разработкой, а также необходимым навыком, чтобы стать фронтенд-архитектурой.


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

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

  • Ускорить браузерную загрузку веб-страниц;
  • Уменьшите избыточную передачу данных, сэкономьте сетевой трафик и пропускную способность;
  • Снизьте нагрузку на сервер и значительно повысьте производительность сайта.

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


Введение в правила кэширования

Для облегчения понимания мы думаем, что у браузера есть кеш-база данных для хранения информации о кеше (на самом деле статические ресурсы кешируются в памяти и на диске). данных кеша, вам нужно запросить сервер, сервер вернет правила кеша и данные, а браузер сохранит правила кеша и данные в базе данных кеша.

缓存流程

Запрашивается при вводе адреса в адресной строке браузераindex.htmlне кэшируется, аindex.htmlДругие ресурсы, запрашиваемые внутри, будут следовать стратегии кэширования.Существуют различные правила кэширования HTTP, которые в основном делятся на две категории в зависимости от необходимости отправки запроса на сервер, принудительного кэширования и согласованного кэширования.


Принудительно кэшировать

1, процесс принудительного кэширования

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

强制缓存流程

2. Заставить кеш определить время истечения срока действия

Итак, как определить, истек ли срок действия кеша? На самом деле он по-прежнему реализуется в соответствии с заголовком ответа сервера при первом обращении к нему.HTTP 1.0версия иHTTP 1.1Версии различаются.

существуетHTTP 1.0версия, поле заголовка ответа, используемое сервером,Expires, значение представляет собой абсолютное время (отметку времени) в будущем, текущее время на момент запроса браузера превышаетExpiresУстановленное время указывает на то, что кеш недействителен, и запрос нужно отправить на сервер повторно, иначе данные будут получены непосредственно из базы данных кеша.

существуетHTTP 1.1версия, поле заголовка ответа, используемое сервером,Cache-Control, имеет несколько значений с разными значениями.

  • частный: клиент может кэшировать;
  • общедоступный: и клиент, и прокси-сервер могут кэшировать (для внешнего интерфейса это можно считать тем же, что иprivateтот же эффект);
  • max-age=xxx: кэшированное содержимое будетxxxИстекает в секундах (относительное время в секундах);
  • без кеша: согласованный кеш (описанный ниже) требуется для проверки того, не просрочены ли данные;
  • no-store: весь контент не будет кэшироваться, ни принудительный кеш, ни согласованный кеш не сработают.

Cache-ControlНаиболее часто используемые значенияmax-age=xxx, сам кеш существует для оптимизации и производительности передачи данных, поэтомуno-storeпочти не используется.

Примечание: вHTTP 1.0версия,ExpiresАбсолютное время поля получается с сервера.Поскольку запрос требует времени, возникает расхождение между временем запроса браузера и временем, полученным сервером при получении запроса, что также приводит к ошибке попадания в кэш.HTTP 1.1версия, потому чтоCache-Controlзначениеmax-age=xxxсерединаxxxПосле того, как относительная единица времени указана в секундах, браузер получает ресурсы для запуска обратного отсчета, чтобы избежатьHTTP 1.0Существует недостаток ошибки в кеше, попадающем в середину, чтобы быть совместимым с более низкой версией протокола HTTP, два заголовка ответа будут использоваться одновременно при нормальной разработке.HTTP 1.1Реализация версии имеет приоритет надHTTP 1.0.


3. Просмотр принудительного кеша через сеть

Открываем NetWork через инструменты разработчика браузера Chrome для просмотра информации о принудительном кэшировании.

Network 缓存响应头

Приведенное выше является ответом изображения логотипа веб-сайта Baidu, мы ясно видим, что оно совместимо сHTTP 1.0а такжеHTTP 1.1версия и хранится с использованием принудительного кеша10год.

Давайте посмотрим на разницу между данными, полученными через кеш, и другими ресурсами в Сети.

Network 缓存的表示

На самом деле кеш хранится в двух местах: в памяти и на диске.Это определяется собственной стратегией текущего браузера, которая является относительно случайной.Будут отображаться данные, извлеченные из кеша памяти.(from memory cache), данные, извлеченные из кеша диска, будут отображаться(from disk cache).


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

// 强制缓存
const http = require("http");
const url = require("url");
const path = require("path");
const mime = require("mime");
const fs = require("fs");

let server = http.createServer((req, res) => {
    let { pathname } = url.parse(req.url, true);
    pathname = pathname !== "/" ? pathname : "/index.html";

    // 获取读取文件的绝对路径
    let p = path.join(__dirname, pathname);

    // 查看路径是否合法
    fs.access(p, err => {
        // 路径不合法则直接中断连接
        if (err) return res.end("Not Found");

        // 设置强制缓存
        res.setHeader("Expires", new Date(Date.now() + 30000).toGMTString());
        res.setHeader("Cache-Control", "max-age=30");

        // 设置文件类型并响应给浏览器
        res.setHeader("Content-Type", `${mime.getType(p)};charset=utf8`);
        fs.createReadStream(p).pipe(res);
    });
});

server.listen(3000, () => {
    console.log("server start 3000");
});

надmimeмодульныйgetTypeМетод может успешно вернуть тип файла, соответствующий файлу во входящем пути, напримерtext/htmlа такжеapplication/javascriptИ т. д., это сторонний модуль, который необходимо установить перед использованием.

npm install mime


Согласовать кеш

1, процесс согласования кэша

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

协商缓存流程

Разница между согласованным кешем и принудительным кешем заключается в том, что согласованный кеш должен связываться с сервером каждый раз, когда делается запрос, а код состояния, возвращаемый сервером кеша обращений, больше не используется.200, но304.

2. Согласуйте флаг оценки кэша

Обязательный кеш предназначен для управления доступом к серверу через время истечения срока действия, а кеш согласования должен каждый раз взаимодействовать с сервером для сравнения идентификатора кеша.HTTP 1.0версия иHTTP 1.1Версии тоже разные.

существуетHTTP 1.0версия, сервер проходитLast-ModifiedЗаголовок ответа используется для установки идентификатора кеша, обычно в качестве значения берется время последней модификации (абсолютное время) запрошенных данных, а браузер получает возвращенные данные и идентификатор и сохраняет их в кеше, и запрос будет автоматически отправлен повторно.If-Modified-SinceЗаголовок запроса, значение — это время последней модификации (идентификатор), возвращенное ранее, и сервер забирает его.If-Modified-SinceЗначение сравнивается со временем последней модификации данных, если время последней модификации большеIf-Modified-SinceЗначение , указывающее, что оно было изменено, затем передатьLast-ModifiedЗаголовок ответа возвращает новое время последнего изменения и новые данные, в противном случае он не изменяется, и возвращается код состояния.304Сообщите браузеру, чтобы он попал в кеш.

существуетHTTP 1.1версия, сервер проходитEtagЗаголовок ответа используется для установки идентификатора кеша (уникальный идентификатор, как отпечаток пальца, правила генерации определяются сервером), браузер получает данные и уникальный идентификатор и сохраняет их в кеше.If-None-MatchЗаголовок запроса приносит серверу уникальный идентификатор, а сервер извлекает уникальный идентификатор и сравнивает его с предыдущим, если он отличается, значит он был изменен, и возвращаются новый идентификатор и данные, если они совпадают, возвращается код состояния.304Сообщите браузеру, чтобы он попал в кеш.

Блок-схема стратегии кэширования согласования HTTP выглядит следующим образом:

协商缓存流程图

Примечание. При использовании согласованного кэшаHTTP 1.0Версия пока не очень надежная.Допустим файл удаляется после добавления одного символа, файл эквивалентен без изменений, но изменилось время последней модификации, это будет считаться модификацией, должно попасть в кеш, но сервер повторно отправляет данные, поэтомуHTTP 1.1используется вEtagУникальный идентификатор генерируется на основе содержимого файла или аннотации, что гарантирует, что до тех пор, пока содержимое файла остается неизменным, кэш будет поражен.HTTP 1.1Реализация версии имеет приоритет надHTTP 1.0.

3. Просмотр кэша согласования через сеть

Мы также открываем NetWork через инструменты разработчика браузера Chrome, чтобы просмотреть информацию о кэше согласования.

Запросите информацию заголовка запроса сервера еще раз:

Network 再次请求服务器的请求头信息

Информация заголовка ответа, попадающая в кэш согласования:

Network 命中协商缓存的响应头信息

Давайте посмотрим на разницу между данными, полученными через кеш согласования, и первой загрузкой в ​​Сети.

Первый запрос:

Network 第一次请求

Запрос после кеша:

Network 缓存后请求

Сравнив два рисунка, мы можем обнаружить, что код состояния, когда кеш согласования вступает в силу, выглядит следующим образом:304И размер пакета и запрошенное время значительно уменьшается, потому что сервер только во время идентификатора возврата, чем последнийheaderчасть, браузер уведомляется об использовании кеша через код состояния, и больше нет необходимости возвращать основную часть сообщения в браузер.

4. Сервер NodeJS реализует кеш согласования

// 协商缓存
const http = require("http");
const url = require("url");
const path = require("path");
const mime = require("mime");
const fs = require("fs");0
const crytpo = require("crytpo");

let server = http.createServer((req, res) => {
    let { pathname } = url.parse(req.url, true);
    pathname = pathname !== "/" ? pathname : "/index.html";

    // 获取读取文件的绝对路径
    let p = path.join(__dirname, pathname);

    // 查看路径是否合法
    fs.stat(p, (err, statObj) => {
        // 路径不合法则直接中断连接
        if (err) return res.end("Not Found");

        let md5 = crypto.createHash("md5"); // 创建加密的转换流
        let rs = fs.createReadStream(p); // 创建可读流

        // 读取文件内容并加密
        rs.on("data", data => md5.update(data));

        rs.on("end", () => {
            let ctime = statObj.ctime.toGMTString(); // 获取文件最后修改时间
            let flag = md5.digest("hex"); // 获取加密后的唯一标识

            // 获取协商缓存的请求头
            let ifModifiedSince = req.headers["if-modified-since"];
            let ifNoneMatch = req.headers["if-none-match"];

            if (ifModifiedSince === ctime || ifNoneMatch === flag) {
                res.statusCode = 304;
                res.end();
            } else {
                // 设置协商缓存
                res.setHeader("Last-Modified", ctime);
                res.setHeader("Etag", flag);

                // 设置文件类型并响应给浏览器
                res.setHeader("Content-Type", `${mime.getType(p)};charset=utf8`);
                rs.pipe(res);
            }
        });
    });
});

server.listen(3000, () => {
    console.log("server start 3000");
});

В приведенном выше коде содержимое файла считывается через читаемый поток и передается черезcryptoмодуль выполненmd5Зашифрованный результат используется в качестве уникального идентификатора, который гарантирует, что, пока содержимое файла остается неизменным, он попадет в кеш, совместимый сHTTP 1.0а такжеHTTP 1.1Две версии, если одна удовлетворена, возвращаются напрямую304Сообщите браузеру, чтобы он попал в кеш.

Примечание: На самом деле не рекомендуется шифровать содержимое прочитанного файла.Если прочитан большой файл, после чтения содержимого файла и выполненияmd5Процесс шифрования занимает очень много времени, поэтому в процессе разработки необходимо выбрать метод, который может гарантировать производительность сервера в соответствии с реальной ситуацией в бизнесе для создания уникального идентификатора, например, в соответствии с резюме файла.


Суммировать

Чтобы сделать стратегию кэширования более надежной и гибкой,HTTP 1.0версия иHTTP 1.1Стратегия кеша версии будет использоваться одновременно, даже принудительный кеш и согласованный кеш будут использоваться одновременно.Для принудительного кеша сервер уведомляет браузер о времени кеша.Во время кеша, следующий запрос будет использовать кэш напрямую. Если допустимое время превышено, будет выполнено согласование. Политика кэширования для согласованного кэша будет кэшировать информацию вEtagа такжеLast-Modifiedчерез заголовок запросаIf-None-Matchа такжеIf-Modified-SinceОтправьте его на сервер, и сервер заодно проверит и установит новый обязательный кеш, а проверка пройдет и вернется304Когда отображается код состояния, браузер напрямую использует кеш. Если кеш согласования также не попадает, сервер сбрасывает идентификатор кеширования согласования.