В последнем блоге мы обсуждали различные типы кэшей и их варианты использования.
В этой статье мы рассмотрим, как мы можем использовать кэширование, используя заголовки HTTP-ответов и поддержку, предоставляемую JAX-RS.
Expires Header
В HTTP 1.0 простой заголовок ответа с именем Expires сообщает браузеру, как долго он может кэшировать объект или страницу. Это будет дата в будущем, после которой кеш будет недействительным. Итак, если мы сделали вызов API для получения данных:
1
|
GET /users/1 |
Заголовок ответа будет:
1
2
3
4
5
|
HTTP /1 .1 200 OK Content-Type: application /xml Expires: Tue, 25 Aug 2013 16:00 GMT ----- <user id = "1" >...< /users > |
Это означает, что данные XML действительны до 25 августа 2013 года, 16:00 часов по Гринвичу.
JAX-RS поддерживает этот заголовок в объекте javax.ws.rs.core.Response .
01
02
03
04
05
06
07
08
09
10
11
12
|
@Path ( "{id}" ) @GET @Produces (MediaType.APPLICATION_XML) public Response getUserXML( @PathParam ( "id" ) Long id){ User user = userDB.get(id); ResponseBuilder builder = Response.ok(user,MediaType.APPLICATION_XML); //Putting expires header for HTTP broswer caching. Calendar cal = Calendar.getInstance(); cal.set( 2013 , 7 , 25 , 16 , 0 ); builder.expires(cal.getTime()); return builder.build(); } |
Но для поддержки CDN, прокси-кешей и повторных проверок требовалось более расширенные заголовки с более широким набором функций, имеющих более явные элементы управления. Следовательно, в HTTP 1.1 было введено несколько новых заголовков, и Expires был исключен. Позволяет исследовать их.
Cache-Control
Cache-Control имеет переменный набор директив, разделенных запятыми, которые определяют, кто, как и как долго может кэшироваться. Давайте рассмотрим несколько из них:
- — private / public : это директивы доступности, private означает, что браузер может кэшировать объект, но прокси или CDN не могут, а public делает его кэшируемым всеми.
- -no-cache, no-store, max-age — это несколько других, в которых имя рассказывает историю.
JAX-RS предоставляет класс javax.ws.rs.core.CacheControl для представления этого заголовка.
01
02
03
04
05
06
07
08
09
10
11
12
13
|
@Path ( "{id}" ) @GET @Produces (MediaType.APPLICATION_XML) public Response getUserXMLwithCacheControl( @PathParam ( "id" ) Long id){ User user = userDB.get(id); CacheControl cc = new CacheControl(); cc.setMaxAge( 300 ); cc.setNoStore( true ); cc.setPrivate( true ); ResponseBuilder builder = Response.ok(user,MediaType.APPLICATION_XML); builder.cacheControl(cc); return builder.build(); } |
Повторная проверка и условные операции GET : после истечения срока действия кэша кэш-память может повторно проверить правильность кэша, отправив запрос на сервер, чтобы проверить, устарел ли кэш или работает ли он. Это делается с помощью заголовка под названием « Last-Modified ».
1
2
3
4
|
HTTP /1 .1 200 OK .... Cache-Control: max-age=1000 Last-Modified: Mon, 19 aug 2013 16:00 IST |
Для повторной проверки необходимо отправить запрос GET с заголовком « If-Modified- Since». Это называется условным GET, в случае изменения данных будет отправлен код ответа 200 (ОК) с текущим значением ресурса. И если данные не изменяются, отправляется код ответа «304», который будет означать, что кэш все еще действителен, в этот момент тег «Last-Modified» может быть обновлен.
Etag
Etag — это еще один HTTP-заголовок, который можно использовать для повторной проверки кэшей. Обычно это значение хеша MD5. Хеш, сгенерированный из ресурса, отправляется сервером в ответе как значение Etag, поэтому при проверке клиент может отправить свое значение Etag на сервер, чтобы проверить, совпадает ли значение, находящееся на сервере. (Поскольку хеш генерируется из ресурса, измените в ресурсе сгенерировал бы другой хеш)
Для этого условного GET отправляется запрос с заголовком «If-none-Match» для проверки.
1
2
|
GET /users/23 HTTP /1 .1 If-None-Match: "23432423423454654667444" |
Кроме того, мы можем иметь сильные и слабые значения Etag в зависимости от различных вариантов использования.
JAX-RS предоставляет нам javax.ws.rs.core.EntityTag для того же.
1
2
3
|
public class EntityTag { ..... ..... |
Чтобы помочь с условными GET, JAX-RS также предоставил один инъецируемый вспомогательный класс Request, который имеет такие методы, как…
1
2
3
4
|
.... ResponseBuilder evalutatePostConditions(EntityTag eTag); ResponseBuilder evaluatePreConditions(Date isLastModified); ..... |
Сравниваются значения etag или LastModified, отправленные в заголовке запроса. Давайте посмотрим на пример …
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
@Path ( "{id}" ) @GET @Produces (MediaType.APPLICATION_XML) public Response getUserWithEtagSupport( @PathParam ( "id" ) Long id, @Context Request request){ User user = userDB.get(id); //generating Etag out of hashCode of user EntityTag tag = new EntityTag(Integer.toString(user.hashCode())); CacheControl cc = new CacheControl(); cc.setMaxAge( 1000 ); ResponseBuilder builder = request.evaluatePreconditions(tag); if (builder!= null ){ //means the preconditions have been met and the cache is valid //we just need to reset the cachecontrol max age (optional) builder.cacheControl(cc); return builder.build(); } //preconditions are not met and the cache is invalid //need to send new value with reponse code 200 (OK) builder = Response.ok(user,MediaType.APPLICATION_XML); //reset cache control and eTag (mandatory) builder.cacheControl(cc); builder.tag(tag); return builder.build(); } |
Если условия были выполнены, возвращается нулевое значение, что означает, что последний тег и тег, указанный в заголовке запроса, совпадают, и нет необходимости отправлять новые данные с ответом OK. «304 ″ ответ означает, что неизмененным является отправка.
Если теги не совпадают, возвращается новый объект RequestBuilder, в котором мы устанавливаем новый etag и текущую версию данных (в данном случае, пользователя).
Вот как с помощью JAX-RS мы можем эффективно использовать HTTP-кеширование в полном объеме.