javascript
spring http缓存_HTTP缓存与Spring示例
spring http緩存
緩存是HTTP協議的強大功能,但由于某些原因,它主要用于靜態(tài)資源,例如圖像,CSS樣式表或JavaScript文件。 但是,HTTP緩存不僅限于應用程序的資產,因為您還可以將其用于動態(tài)計算的資源。
只需少量工作,您就可以加快應用程序的速度并改善整體用戶體驗。 在本文中,您將學習如何對Spring控制器的結果使用內置的HTTP響應緩存機制 。
1.如何以及何時使用HTTP響應緩存?
您可以在應用程序的多層上進行緩存。 數據庫有其緩存存儲, 應用程序將一些數據緩存在操作內存中 ,Web客戶端也在其側重用信息。
您可能知道,HTTP協議負責網絡通信。 緩存機制使我們可以通過減少客戶端與服務器之間傳輸的數據量來優(yōu)化網絡流量。
您可以(并且應該)優(yōu)化什么?
當網絡資源不經常更改或您確切知道其更新時間時 ,您便可以使用HTTP緩存進行優(yōu)化。
一旦確定了用于HTTP緩存的競爭者,就需要選擇一種合適的方法來管理緩存的驗證。 HTTP協議定義了幾個請求和響應頭,可用于控制客戶端何時應清除緩存 。
適當的HTTP標頭的選擇取決于您要優(yōu)化的特定情況。 但是,無論用例如何,我們都可以根據緩存的驗證位置劃分緩存管理選項。 它可以由客戶端或服務器驗證。
讓我們繼續(xù)上演這個節(jié)目。
2.客戶端緩存驗證
當您知道請求的資源在給定時間內不會更改時,服務器可以將此類信息作為響應頭發(fā)送給客戶端。 根據該信息,客戶端決定是應該再次獲取資源還是重用先前下載的資源。
有兩個可能的選項來描述客戶端何時應再次獲取資源并刪除存儲的緩存值。 因此,讓我們看看它們的實際效果。
HTTP緩存在固定時間內有效
如果要防止客戶端在給定的時間內重新獲取資源 ,則應查看Cache-Control標頭,您可以在其中指定應將提取的數據重用多長時間。
通過將標頭的值設置為max-age = <seconds>,您可以通知客戶端不必再提取資源多長時間。 緩存值的有效性與請求時間有關。
為了在Spring的控制器中設置HTTP標頭,而不是常規(guī)的有效負載對象,您應該返回ResponseEntity包裝器類 。 這是一個例子:
@GetMapping("/{id}") ResponseEntity<Product> getProduct(@PathVariable long id) {// …CacheControl cacheControl = CacheControl.maxAge(30, TimeUnit.MINUTES);return ResponseEntity.ok().cacheControl(cacheControl).body(product); }標頭的值只是一個常規(guī)的String,但是在Cache-Control的情況下,Spring為我們提供了一個特殊的構建器類,該類可防止我們犯小錯誤,如拼寫錯誤。
HTTP緩存有效至固定日期
有時您知道什么時候資源會改變。 這是經常發(fā)布的數據的常見情況,例如天氣預報或為昨天的交易時段計算的股市指標。 資源的確切到期日期可以向客戶端公開。
為此,您應該使用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("/forecast") ResponseEntity<Forecast> getTodaysForecast() {// ...ZonedDateTime expiresDate = ZonedDateTime.now().with(LocalTime.MAX);String expires = expiresDate.format(DateTimeFormatter.RFC_1123_DATE_TIME);return ResponseEntity.ok().header(HttpHeaders.EXPIRES, expires).body(weatherForecast); }注意, HTTP日期格式需要有關時區(qū)的信息 。 這就是為什么上面的示例使用ZonedDateTime的原因 。 如果嘗試使用LocalDateTime ,則在運行時將出現以下錯誤消息:
java.time.temporal.UnsupportedTemporalTypeException: Unsupported field: OffsetSeconds如果響應中同時存在Cache-Control和Expires標頭,則客戶端僅使用Cache-Control 。
3.服務器端緩存驗證
在基于用戶輸入動態(tài)生成的內容中,服務器不知道何時更改請求的資源的情況更為常見。 在這種情況下,客戶端可以使用先前獲取的數據,但首先,它需要詢問服務器該數據是否仍然有效。
自日期以來是否修改過資源?
如果您跟蹤Web資源的修改日期,則可以將該日期作為響應的一部分向客戶端公開。 在下一個請求中,客戶端會將這個日期發(fā)送回服務器,以便它可以驗證自上一個請求以來是否已修改資源。 如果資源沒有更改,則服務器不必再次重新發(fā)送數據。 相反,它使用304 HTTP代碼進行響應,而沒有任何有效負載。
要公開資源的修改日期,您應該設置Last-Modified標頭。 Spring的ResponseEntity構建器具有一個名為lastModified()的特殊方法 ,該方法可以幫助您以正確的格式分配值。 您將在一分鐘內看到它。
但是, 在發(fā)送完整響應之前,應檢查客戶端是否在請求中包括 If-Modified-Since 標頭 。 客戶端根據與該特定資源的先前響應一起發(fā)送的Last-Modified標頭的值來設置其值。
如果If-Modified-Since標頭的值與請求資源的修改日期匹配,則可以節(jié)省一些帶寬并以空主體響應客戶端。
再次,Spring提供了一個輔助方法,該方法簡化了上述日期的比較。 可以在WebRequest包裝器類中找到稱為checkNotModified()的方法,您可以將其添加到控制器的方法中作為輸入。
聽起來復雜嗎?
讓我們仔細看一下完整的示例。
@GetMapping("/{id}") ResponseEntity<Product> getProduct(@PathVariable long id, WebRequest request) {Product product = repository.find(id);long modificationDate = product.getModificationDate().toInstant().toEpochMilli();if (request.checkNotModified(modificationDate)) {return null;}return ResponseEntity.ok().lastModified(modificationDate).body(product); }首先,我們獲取請求的資源并訪問其修改日期。 我們將日期轉換為格林尼治標準時間1970年1月1日以來的毫秒數,因為這是Spring框架所期望的格式。
然后,我們將日期與If-Modified-Since標頭的值進行比較,并在正匹配項上返回一個空主體。 否則,服務器將發(fā)送完整的響應正文,并帶有相應的Last-Modified頭值。
有了這些知識,您幾乎可以涵蓋所有常見的緩存候選對象。 但是,您應該知道還有一個更重要的機制是……
使用ETag進行資源版本控制
到目前為止,我們將有效期的精度定義為一秒的精度。
但是,如果您需要比一秒鐘更高的精度呢?
這就是ETag進入的地方。
可以將ETag定義為唯一的字符串值,該字符串值可以在時間點上明確標識資源。 通常,服務器根據給定資源的屬性或最新的修改日期(如果可用)來計算ETag。
客戶端和服務器之間的通信流程幾乎與修改日期檢查的情況相同。 僅標題的名稱和值不同。
服務器設置在所謂的(令人驚奇地) 的ETag報頭中的ETag值。 當客戶端再次訪問資源時,它應該在名為If-None-Match的標頭中發(fā)送其值。 如果該值與資源的新計算的ETag匹配,則服務器可以使用空主體和HTTP代碼304進行響應。
在Spring中,您可以實現ETag服務器流程,如下所示:
@GetMapping("/{id}") ResponseEntity<Product> getProduct(@PathVariable long id, WebRequest request) {Product product = repository.find(id);String modificationDate = product.getModificationDate().toString();String eTag = DigestUtils.md5DigestAsHex(modificationDate.getBytes());if (request.checkNotModified(eTag)) {return null;}return ResponseEntity.ok().eTag(eTag).body(product); }看起來相似嗎?
是的,樣本與修改日期檢查的前一個樣本幾乎相同。 我們只是使用一個不同的值進行比較(并使用MD5算法來計算ETag)。 注意, WebRequest 有一個重載的 checkNotModified() 方法來處理表示為字符串的ETag。
如果Last-Modified和ETag的工作原理幾乎相同,為什么我們需要兩者?
最后修改與ETag
正如我已經提到的, Last-Modified標頭的精度較低,因為它的精度為一秒鐘。 為了獲得更高的精度,請選擇ETag 。
當您不跟蹤資源的修改日期時,還必須使用ETag 。 服務器可以基于資源的屬性來計算其值。 可以將其視為對象的哈希碼。
如果資源具有其修改日期,并且一秒精度適合您,請使用Last-Modified標頭。 為什么? 因為ETag計算可能是一項昂貴的操作 。
順便說一句,值得一提的是,HTTP協議沒有指定用于計算ETag的算法。 選擇算法時,您應該專注于其速度。
本文重點介紹緩存GET請求,但您應該知道服務器可以使用ETag同步更新操作。 但這是另一篇文章的想法。
Spring ETag過濾器
由于ETag只是內容的字符串表示形式,因此服務器可以使用響應的字節(jié)表示形式來計算其值。 這意味著您實際上可以將ETag分配給任何響應。
你猜怎么著?
Spring框架為您提供了ETag響應過濾器實現 ,該實現為您完成。 您要做的就是在應用程序中配置過濾器。
在Spring應用程序中添加HTTP過濾器的最簡單方法是通過配置類中的FilterRegistrationBean 。
@Bean public FilterRegistrationBean filterRegistrationBean () {ShallowEtagHeaderFilter eTagFilter = new ShallowEtagHeaderFilter();FilterRegistrationBean registration = new FilterRegistrationBean();registration.setFilter(eTagFilter);registration.addUrlPatterns("/*");return registration; }在這種情況下,對addUrlPatterns()的調用是多余的,因為默認情況下所有路徑都匹配。 我將其放在此處以演示您可以控制Spring應該將ETag值添加到哪些資源。
除了生成ETag之外,過濾器還盡可能以HTTP 304和空主體作為響應。
但是要當心。
ETag計算可能很昂貴。 對于某些應用程序,啟用此過濾器實際上可能造成弊大于利的后果 。 使用它之前,請先考慮一下您的解決方案。
結論
這篇文章似乎很長,但是我們涵蓋了很多有用的材料。 現在,您知道了如何使用HTTP緩存優(yōu)化應用程序,以及哪種方法最適合您,因為應用程序具有不同的需求。
您了解到客戶端緩存驗證是最有效的方法,因為不涉及數據傳輸。 如果適用,您應該始終支持客戶端緩存驗證。
我們還討論了服務器端驗證,并比較了Last-Modified和ETag標頭。 最后,您了解了如何在Spring應用程序中設置全局ETag過濾器。
希望本文對您有所幫助。 如果您喜歡,請在下面分享或寫下您的評論。 另外,請告訴我是否可以改善或擴展內容。 我很想知道你的想法。
翻譯自: https://www.javacodegeeks.com/2018/10/http-cache-spring-examples.html
spring http緩存
總結
以上是生活随笔為你收集整理的spring http缓存_HTTP缓存与Spring示例的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 嘉华预售价格(嘉华备案价)
- 下一篇: spring boot分层_只需5分钟即