Статьи

HTTP-кеширование с использованием JAX-RS

В последнем блоге мы обсуждали различные типы кэшей и их варианты использования.
В этой статье мы рассмотрим, как мы можем использовать кэширование, используя заголовки 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-кеширование в полном объеме.

Ссылка: HTTP-кэширование с использованием JAX-RS от нашего партнера JCG Анируд Бхатнагар в блоге anirudh bhatnagar .