Кэширование Spring Boot с ContentVersionStrategy запрещает сжатие ресурсов с помощью gzip - PullRequest
0 голосов
/ 24 февраля 2020

У нас есть веб-приложение Spring Boot с Thymeleaf. В шаблонах HTML мы ссылаемся на некоторые ресурсы * stati c, например /src/main/resources/static/js/main.js через <script defer th:src="@{/js/main.js}"></script>.

Чтобы позволить браузерам кэшировать ресурсы stati c для нескольких посещений нашего веб-сайта, мы включить управление версиями контента:

spring.resources:
    chain:
      strategy.content:
        enabled: true
        paths: /**
    cache.cachecontrol.max-age: 365d

Все это отлично работает, и мы получаем ресурс с его MD5 га sh, добавленным к имени файла (например, /main-d9f17fd70ee583fef4acf26dd331b8ab.js).

Для дальнейшего уменьшения трафика c том, теперь мы хотим включить сжатие ресурсов с помощью gzip:

server:
  compression:
    enabled: true
    mime-types: application/javascript,and-some-others
    min-response-size: 1024

При запросе (версионного) ресурса с заголовком Accept-Encoding='gzip' мы не получаем ответ с Content-Encoding='gzip'. Следовательно, сжатие ресурсов, по-видимому, не работает в сочетании с управлением версиями содержимого.

Если мы отключаем управление версиями содержимого, сжатие ресурсов работает просто отлично: заголовок Content-Encoding='gzip' устанавливается для (теперь не версионного) ресурса.

Итак, мы покопались во внутренностях Spring и обнаружили следующее:

  1. org.springframework.web.servlet.resource.VersionResourceResolver#getResponseHeaders всегда устанавливает (сильный) заголовок ETag:
public HttpHeaders getResponseHeaders() {
    HttpHeaders headers = (this.original instanceof HttpResource ?
            ((HttpResource) this.original).getResponseHeaders() : new HttpHeaders());
    headers.setETag("\"" + this.version + "\"");
    return headers;
}
org.apache.coyote.CompressionConfig#useCompression отключает сжатие, если существует сильный ETag:
public boolean useCompression(Request request, Response response) {
    ...
    if (noCompressionStrongETag) {
        String eTag = responseHeaders.getHeader("ETag");
        if (eTag != null && !eTag.trim().startsWith("W/")) {
            // Has an ETag that doesn't start with "W/..." so it must be a
            // strong ETag
            return false;
        }
    }
    ...
}

Вы можете установить noCompressionStrongETag в false, но это устарело и будет удалено с помощью Tomcat 10 .. .

Чтобы продемонстрировать проблему, я создал пример проекта в Github с тремя проходными тестами и одним провальным тестом, который показывает, где наши ожидания не оправдываются ...

У вас есть идея, как решить это противоречие? Мы что-то делаем неправильно?

1 Ответ

0 голосов
/ 25 апреля 2020

Эта проблема теперь отслеживается в репозитории Spring's Github . В настоящее время идея состоит в том, чтобы перейти от сильных ETag к слабым ETag весной VersionResourceResolver. Я обновлю этот ответ, как только проблема будет решена.

Участник, выделенный в проблеме, указывает, что текущее поведение связано с исправлением в Tomcat, адресуемому здесь . Там был сделан вывод, что сжатие ответа нарушает значение сильных ETag и, таким образом, является ошибкой. Поэтому Tomcat применяет сжатие только в том случае, если отсутствует или присутствует только слабый заголовок ETag (что согласуется с моими наблюдениями, описанными в вопросе SO).

...