Spring MVCでのhttp Caching


Spring MVCでのhttp Caching
CacheはHTTPプロトコルにおける非常に重要な機能であり、Cacheを使用することでアプリケーションの性能を大幅に向上させ、データのネットワーク伝送を低減することができる.
通常、画像、CSS、JSファイルなどの静的リソースをキャッシュします.同様に、HTTP CacheとSpring MVCを組み合わせてダイナミックリソースのキャッシュを行うことができます.
では、ダイナミックリソースのキャッシュはいつ使用されますか?
このリソースが頻繁に更新されていない場合や、そのリソースがいつ更新されるかを正確に知っている場合にのみHTTP Cacheを使用することができます.
HTTP Cacheはリクエストヘッダによって実現され,主に3つの方式がある:期限切れ時間,最終更新時間,Etag.
有効期限はクライアント認証であり、最終更新時間とEtagはサーバ側認証である.
有効期限
期限切れにはCache-ControlとExpiresヘッダの2つの方法があります.
Cache-Controlでは、maxAgeを設定できます.この時間を超えると、リソースが再要求されます.次のようになります.
@GetMapping("/{id}")
ResponseEntity getProduct(@PathVariable long id) {
   // …
   CacheControl cacheControl = CacheControl.maxAge(30, TimeUnit.MINUTES);
   return ResponseEntity.ok()
           .cacheControl(cacheControl)
           .body(product);
}

HeadでExpiresプロパティを設定することもできます.Expiresの時間は、次のような標準的な時間フォーマットが必要です.
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 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);
}

Cache-ControlとExpiresが同時に表示される場合は、Cache-Controlが優先されます.
Last-Modified
その検証ロジックは、クライアントが前回要求したLast-Modifiedに基づいてIf-Modified-Sinceを設定し、サーバ側がこのプロパティを受信した後、前のものと比較することができ、同じ場合は空のbodyを返すことができる.次のようになります.
@GetMapping("/{id}")
ResponseEntity 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);
}

ETag
Last-Modifiedの時間は秒までしか正確ではありませんが、もっと細粒度が必要ならETagが必要です.
ETagは、現在の時点であるリソースの唯一のタグと見なすことができ、そのリソースのhash値をETagとして取得することができます.
次のような実装があります.
@GetMapping("/{id}")
ResponseEntity 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);
}

Spring ETag filter
SpringはShallowEtagHeaderFilterを提供し、返された内容に基づいて自動的にEtagを生成します.
@Bean
public FilterRegistrationBean filterRegistrationBean () {
   ShallowEtagHeaderFilter eTagFilter = new ShallowEtagHeaderFilter();
   FilterRegistrationBean registration = new FilterRegistrationBean();
   registration.setFilter(eTagFilter);
   registration.addUrlPatterns("/*");
   return registration;
}

ETag計算は性能に影響を与える可能性があることに注意してください.
チュートリアルの詳細については、flydeanのブログを参照してください.