在Spring Boot中實現HTTP緩存的方法

緩存是HTTP協議的一個強大功能,但由於某些原因,它主要用於靜態資源,如圖像,CSS樣式表或JavaScript文件。本文重點給大家介紹在Spring Boot中實現HTTP緩存的方法,感興趣的朋友跟隨小編一起看看吧

緩存是HTTP協議的一個強大功能,但由於某些原因,它主要用於靜態資源,如圖像,CSS樣式表或JavaScript文件,但是,HTTP緩存不僅限於這些,還可以將其用於動態計算的資源。

通過少量工作,您可以加快應用程序並改善整體用戶體驗。在本文中,您將學習 如何使用內置的HTTP響應緩存機制來實現緩存SpringBoot控制器的結果 。

1.如何以及何時使用HTTP響應緩存?

您可以在應用程序的多個層上進行緩存。數據庫具有其緩存存儲,Web客戶端也在其需要重用的信息。HTTP協議負責網絡通信。緩存機制允許我們通過減少客戶端和服務器之間傳輸的數據量來優化網絡流量。

何時優化: 當Web資源不經常更改或您確切知道何時更新時 ,就可以使用HTTP緩存進行優化。一旦確定了HTTP緩存的競爭者,就需要選擇合適的方法來管理緩存的驗證。 HTTP協議定義了幾個請求和響應標頭,您可以使用它們來控制客戶端何時清除緩存 。

選擇適當的HTTP標頭取決於您要優化的特定情況。但是無論用例如何,我們可以根據緩存的驗證發生在哪裏進行緩存管理選項的劃分。

2.客戶端緩存驗證

當您知道請求的資源在給定的時間內不會更改時,服務器可以將此類信息作爲響應標頭髮送到客戶端。基於該信息,客戶端決定是否應該再次獲取資源或重用先前下載的資源。

有兩種可能的選項可以描述客戶端何時應該再次獲取資源並刪除存儲的緩存值。所以讓我們看看他們是如何運行的。

HTTP緩存在固定的時間內有效:如果要 阻止客戶端在指定時間內重新獲取資源 ,則應該使用 Cache-Control 標頭,可以在其中指定應該重新獲取所獲取數據的時間。

通過將標頭的值設置爲 max-age = <seconds>, 可以通知客戶端多長時間不再需要再次獲取資源。 緩存值的有效性與請求的時間有關。

爲了設置在Spring的控制器中的HTTP標頭,就要在RESTContoller用 ResponseEntity 包裝類 。

@GetMapping(<font>"/{id}"</font><font>)
ResponseEntity<Product> getProduct(@PathVariable <b>long</b> id) {
  </font><font><i>// …</i></font><font>
  CacheControl cacheControl = CacheControl.maxAge(30, TimeUnit.MINUTES);
  <b>return</b> ResponseEntity.ok()
      .cacheControl(cacheControl)
      .body(product);
}
</font>

HTTP標頭的值只是一個常規字符串,但是 Cache-Control Spring爲我們提供了一個特殊的構建器類,它可以防止我們犯下像拼寫錯誤這樣的小錯誤。

HTTP緩存有效到固定日期:有時您知道資源何時會發生變化。對於公佈的數據而言,這是常見的情況,如天氣預報或昨天交易時段計算的股市指標。 資源的確切到期日期可以向客戶端公開。 應該使用 Expires HTTP標頭。應使用標準化數據格式 之一格式化日期值。

Sun, 06 Nov 1994 08:49:37 GMT  ; RFC 822, updated by RFC 1123

Sunday, 06-Nov-94 08:49:37 GMT ; RFC 850, obsoleted by RFC 1036

Sun Nov  6 08:49:37 1994       ; ANSI C's asctime() format

幸運的是,Java附帶了第一個這些格式的預定義格式化程序。可以在下面找到將標題設置爲當天結束的示例。

@GetMapping(<font>"/forecast"</font><font>)
ResponseEntity<Forecast> getTodaysForecast() {
  </font><font><i>// ...</i></font><font>
  ZonedDateTime expiresDate = ZonedDateTime.now().with(LocalTime.MAX);
  String expires = expiresDate.format(DateTimeFormatter.RFC_1123_DATE_TIME);
  <b>return</b> ResponseEntity.ok()
      .header(HttpHeaders.EXPIRES, expires)
      .body(weatherForecast);
}
</font>

請注意, HTTP日期格式需要有關時區的信息 。這就是上面的例子使用 ZonedDateTime的原因 。如果您嘗試使用 LocalDateTime ,則最終會在運行時出現以下錯誤消息:

java.time.temporal.UnsupportedTemporalTypeException:不支持的字段:OffsetSeconds

如果響應中存在 Cache-Control 和 Expires 標頭,則客戶端僅使用 Cache-Control 。

3.服務器端緩存驗證

在基於用戶輸入的動態生成的內容中,更常見的是服務器不知道何時將改變所請求的資源。在這種情況下,客戶端可以使用先前獲取的數據,但首先,它需要詢問服務器該數據是否仍然有效。

自第一次握手以來資源是否被修改?如果跟蹤Web資源的修改日期,則可以將此類日期作爲響應的一部分公開給客戶端。在下一個請求中,客戶端將此日期發送回服務器,以便它可以驗證自上一個請求以來資源是否已被修改。如果資源未更改,則服務器不必再次重新發送數據。相反,它使用304 HTTP代碼響應,沒有任何有效負載。

要公開資源的修改日期,您應該設置 Last-Modified 標頭。Spring的ResponseEntity構建器有一個名爲 lastModified() [url=https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/http/ResponseEntity.HeadersBuilder.html#lastModified-long-]的特殊方法[/url],它可以幫助您以正確的格式分配值。你會在一分鐘內看到這一點。

但 在發送完整響應之前,應檢查客戶端是否 在請求中 包含 If-Modified-Since 標頭 。客戶端根據 Last-Modified 標頭的值設置其值,該標頭是與此特定資源的先前響應一起發送的。

如果 If-Modified-Since 標頭的值與所請求資源的修改日期匹配,則可以節省一些帶寬並使用空主體響應客戶端。

Spring再次提供了一個輔助方法,簡化了上述日期的比較。這個名爲 checkNotModified()的 方法可以在 WebRequest 包裝器類中找到,您可以將其作爲輸入添加到控制器的方法中。

讓我們仔細看看完整的例子。

@GetMapping(<font>"/{id}"</font><font>)
ResponseEntity<Product> getProduct(@PathVariable <b>long</b> id, WebRequest request) {
  Product product = repository.find(id);
  <b>long</b> modificationDate = product.getModificationDate()
      .toInstant().toEpochMilli();
 
  <b>if</b> (request.checkNotMod<b>if</b>ied(mod<b>if</b>icationDate)) {
    <b>return</b> <b>null</b>;
  }
 
  <b>return</b> ResponseEntity.ok()
      .lastModified(modificationDate)
      .body(product);
}
</font>

首先,我們獲取所請求的資源並訪問其修改日期。我們將日期轉換爲自格林威治標準時間1970年1月1日以來的毫秒數,因爲這是Spring框架期望的格式。

然後,我們將日期與 If-Modified-Since 標頭的值進行比較,並在正匹配上返回一個空。否則,服務器發送具有 Last-Modified 標頭的適當值的完整響應主體。

憑藉所有這些知識,您幾乎可以涵蓋所有常見的緩存設置選項。但是有一個更重要的機制你應該知道的是......

使用ETag進行資源版本控制

到目前爲止,我們定義了有效期的精確度,精確度爲1秒。但是如果你需要 更好的精度而不僅僅是一秒 呢?這就是ETag的用武之地。

可以將ETag定義爲唯一的字符串值,該值在該時間點明確地標識資源。 通常,服務器根據給定資源的屬性計算ETag,或者,如果可用,則計算其最新修改日期。

客戶端和服務器之間的通信流程與修改日期檢查的情況幾乎相同。只有標題的名稱和值不同。

服務器在名爲 ETag 的標題中設置ETag值。當客戶端再次訪問資源時,它應該在名爲 If-None-Match 的頭中發送其值。如果該值與資源的新計算的ETag匹配,則服務器可以使用空內容和HTTP代碼304進行響應。

在Spring中,您可以實現ETag服務器流程,如下所示:

@GetMapping(<font>"/{id}"</font><font>)
ResponseEntity<Product> getProduct(@PathVariable <b>long</b> id, WebRequest request) {
  Product product = repository.find(id);
  String modificationDate = product.getModificationDate().toString();
  String eTag = DigestUtils.md5DigestAsHex(modificationDate.getBytes());
 
  <b>if</b> (request.checkNotMod<b>if</b>ied(eTag)) {
    <b>return</b> <b>null</b>;
  }
 
  <b>return</b> ResponseEntity.ok()
      .eTag(eTag)
      .body(product);
}
</font>

與前一個樣本幾乎相同,並且修改日期檢查。我們只是使用不同的值進行比較(以及MD5算法來計算ETag)。 請注意, WebRequest 有一個重載的 checkNotModified() 方法來處理表示爲字符串的ETag。

如果 Last-Modified 和 ETag 工作幾乎相同,爲什麼我們需要兩者嗎?

Last-Modified vs ETag

正如我已經提到的, Last-Modified 標頭 不太精確, 因爲它具有一秒的精度。爲了獲得更高的精度,請選擇 ETag 。

當您不跟蹤 資源 的修改日期 時,您也被迫使用 ETag 。服務器可以根據資源的屬性計算其值。將其視爲對象的哈希碼。

如果資源具有其修改日期並且您可以使用一秒精度,請使用 Last-Modified 標頭。爲什麼?因爲 ETag計算可能是一項昂貴的操作 。

順便提一下,值得一提的是HTTP協議沒有指定用於計算ETag的算法。選擇算法時,您應該關注它的速度。

本文重點介紹緩存GET請求,但您應該知道服務器可以使用 ETag 來同步更新操作。

Spring ETag過濾器

因爲ETag只是內容的字符串表示,所以服務器可以使用響應的字節表示來計算其值。意思是 你可以實際將ETag分配給任何響應。

Spring框架爲您提供了ETag響應過濾器實現 ,它可以爲您完成。您所要做的就是在應用程序中配置過濾器。

在Spring應用程序中添加HTTP過濾器的最簡單方法是通過配置類中的 FilterRegistrationBean 。

@Bean
<b>public</b> FilterRegistrationBean filterRegistrationBean () {
  ShallowEtagHeaderFilter eTagFilter = <b>new</b> ShallowEtagHeaderFilter();
  FilterRegistrationBean registration = <b>new</b> FilterRegistrationBean();
  registration.setFilter(eTagFilter);
  registration.addUrlPatterns(<font>"/*"</font><font>);
  <b>return</b> registration;
}
</font>

在這種情況下,對 addUrlPatterns() 的調用是多餘的,因爲默認情況下所有路徑都匹配。我把它放在這裏證明你可以控制Spring應該添加ETag值的資源。

除了ETag生成之外,過濾器還會在可能的情況下響應HTTP 304和空體內容。

但要注意。

ETag計算可能很昂貴。 對於某些應用程序啓用此過濾器實際上可能會導致弊大於利 。在使用之前考慮一下您的解決方案。

結論

現在您已瞭解如何使用HTTP緩存優化應用程序,哪種方法最適合您,因爲應用程序有不同的需求。

您瞭解到客戶端緩存驗證是最有效的方法,因爲不涉及數據傳輸。在適用時,您應該始終支持客戶端緩存驗證。

我們還討論了服務器端驗證並比較了 Last-Modified 和 ETag 標頭。最後,您瞭解瞭如何在Spring應用程序中設置全局ETag過濾器。

總結

以上所述是小編給大家介紹的在Spring Boot中實現HTTP緩存的方法,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時回覆大家的。在此也非常感謝大家對神馬文庫網站的支持!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章