溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

在Spring Boot中實現(xiàn)HTTP緩存的方法

發(fā)布時間:2020-08-19 13:39:21 來源:腳本之家 閱讀:236 作者:banq 欄目:編程語言

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

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

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

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

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

選擇適當?shù)腍TTP標頭取決于您要優(yōu)化的特定情況。但是無論用例如何,我們可以根據緩存的驗證發(fā)生在哪里進行緩存管理選項的劃分。

2.客戶端緩存驗證

當您知道請求的資源在給定的時間內不會更改時,服務器可以將此類信息作為響應標頭發(fā)送到客戶端?;谠撔畔?,客戶端決定是否應該再次獲取資源或重用先前下載的資源。

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

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

通過將標頭的值設置為 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標頭的值只是一個常規(guī)字符串,但是 Cache-Control Spring為我們提供了一個特殊的構建器類,它可以防止我們犯下像拼寫錯誤這樣的小錯誤。

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

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日期格式需要有關時區(qū)的信息 。這就是上面的例子使用 ZonedDateTime的原因 。如果您嘗試使用 LocalDateTime ,則最終會在運行時出現(xiàn)以下錯誤消息:

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

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

3.服務器端緩存驗證

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

自第一次握手以來資源是否被修改?如果跟蹤Web資源的修改日期,則可以將此類日期作為響應的一部分公開給客戶端。在下一個請求中,客戶端將此日期發(fā)送回服務器,以便它可以驗證自上一個請求以來資源是否已被修改。如果資源未更改,則服務器不必再次重新發(fā)送數(shù)據。相反,它使用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],它可以幫助您以正確的格式分配值。你會在一分鐘內看到這一點。

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

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

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日以來的毫秒數(shù),因為這是Spring框架期望的格式。

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

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

使用ETag進行資源版本控制

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

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

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

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

在Spring中,您可以實現(xiàn)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協(xié)議沒有指定用于計算ETag的算法。選擇算法時,您應該關注它的速度。

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

Spring ETag過濾器

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

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

在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計算可能很昂貴。 對于某些應用程序啟用此過濾器實際上可能會導致弊大于利 。在使用之前考慮一下您的解決方案。

結論

現(xiàn)在您已了解如何使用HTTP緩存優(yōu)化應用程序,哪種方法最適合您,因為應用程序有不同的需求。

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

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

總結

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

向AI問一下細節(jié)

免責聲明:本站發(fā)布的內容(圖片、視頻和文字)以原創(chuàng)、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI