В последнем блоге мы обсуждали различные типы кэшей и их варианты использования.
В этой статье мы рассмотрим, как мы можем использовать кэширование, используя заголовки HTTP-ответов и поддержку, предоставляемую JAX-RS.
Expires Header
В HTTP 1.0 простой заголовок ответа с именем Expires сообщает браузеру, как долго он может кэшировать объект или страницу. Это будет дата в будущем, после которой кеш будет недействительным. Итак, если мы сделали вызов API для получения данных:
|
1
|
GET /users/1 |
Заголовок ответа будет:
|
1
2
3
4
5
|
HTTP/1.1 200 OKContent-Type: application/xmlExpires: 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=1000Last-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.1If-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-кеширование в полном объеме.