Статьи

Как сжать ответы в Java REST API с помощью GZip и Jersey

Могут быть случаи, когда ваш REST API предоставляет очень длинные ответы, и мы все знаем, насколько важны скорость передачи и пропускная способность на мобильных устройствах / сетях. Я думаю, что это первая точка оптимизации производительности, которую необходимо учитывать при разработке REST API, поддерживающих мобильные приложения. Угадай, что? Поскольку ответы являются текстовыми, мы можем их сжать. И с сегодняшней мощью смартфонов и планшетов распаковка их на стороне клиента не должна быть большой проблемой … Так что в этой статье я покажу, как ВЫБРАТЬ СЕКТОРНО, сжимая свои ответы REST API, если вы построили их на Java с Джерси , что такое эталонная реализация JAX-RS (и не только)…

1. Джерси фильтры и перехватчики

Что ж, благодаря мощным функциям фильтров и перехватчиков Джерси, реализация довольно проста. Принимая во внимание, что фильтры в первую очередь предназначены для манипулирования параметрами запроса и ответа, такими как заголовки HTTP, URI и / или методы HTTP, перехватчики предназначены для манипулирования сущностями посредством манипулирования потоками ввода / вывода сущностей.

Вы видели силу фильтров в моих сообщениях:

но для сжатия буду использовать GZip WriterInterceptor . Перехватчик записи используется в случаях, когда сущность записывается в «провод», что на стороне сервера, как в данном случае, означает при записи ответной сущности.

1.1. GZip Writer Interceptor

Итак, давайте посмотрим на наш GZip Writer Interceptor:

GZip Writer Interceptor

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
package org.codingpedia.demo.rest.interceptors;
 
import java.io.IOException;
import java.io.OutputStream;
import java.util.zip.GZIPOutputStream;
 
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.MultivaluedMap;
import javax.ws.rs.ext.WriterInterceptor;
import javax.ws.rs.ext.WriterInterceptorContext;
 
@Provider
@Compress
public class GZIPWriterInterceptor implements WriterInterceptor {
      
    @Override
    public void aroundWriteTo(WriterInterceptorContext context)
                    throws IOException, WebApplicationException {
         
        MultivaluedMap<String,Object> headers = context.getHeaders();
        headers.add("Content-Encoding", "gzip");
         
        final OutputStream outputStream = context.getOutputStream();
        context.setOutputStream(new GZIPOutputStream(outputStream));
        context.proceed();
    }
}

Замечания:

  • он реализует WriterInterceptor , который является интерфейсом для перехватчиков javax.ws.rs.ext.MessageBodyWriter.writeTo тела сообщения, которые обертывают вызовы javax.ws.rs.ext.MessageBodyWriter.writeTo
  • провайдеры, реализующие контракт WriterInterceptor должны быть или программно зарегистрированы во время выполнения JAX-RS, или должны быть аннотированы аннотацией @Provider для автоматического обнаружения во время выполнения JAX-RS во время фазы сканирования провайдера.
  • @Compress — аннотация привязки имени, которую мы обсудим более подробно в следующем абзаце.
  • «Перехватчик получает выходной поток из WriterInterceptorContext и устанавливает новый, который является оберткой GZIP исходного выходного потока. После выполнения всех перехватчиков выходной поток, последний раз установленный в WriterInterceptorContext, будет использоваться для сериализации объекта. В приведенном выше примере байты объекта будут записаны в GZIPOutputStream, который будет сжимать данные потока и записывать их в исходный поток вывода. Исходный поток всегда является потоком, который записывает данные в «провод». Когда перехватчик используется на сервере, исходный поток вывода является потоком, в который записываются данные в основной контейнерный поток сервера, который отправляет ответ клиенту ». [2]
  • «Переопределенный метод aroundWriteTo () получает WriterInterceptorContext в качестве параметра. Этот контекст содержит методы получения и установки параметров заголовка, свойств запроса, объекта, потока объектов и других свойств ». [2]; когда вы сжимаете свой ответ, вы должны установить заголовок «Content-Encoding» на «gzip»

1.2. Сжать аннотацию

Фильтры и перехватчики могут быть привязаны к имени . Привязка имени — это концепция, которая позволяет сказать среде выполнения JAX-RS, что определенный фильтр или перехватчик будет выполняться только для определенного метода ресурса. Когда фильтр или перехватчик ограничен только определенным методом ресурса, мы говорим, что он привязан к имени . Фильтры и перехватчики, которые не имеют такого ограничения, называются глобальными . В нашем случае мы создали аннотацию @Compress:

Сжать аннотацию

01
02
03
04
05
06
07
08
09
10
11
package org.codingpedia.demo.rest.interceptors;
 
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
 
import javax.ws.rs.NameBinding;
 
//@Compress annotation is the name binding annotation
@NameBinding
@Retention(RetentionPolicy.RUNTIME)
public @interface Compress {}

и использовал его для пометки методов на ресурсах, которые должны быть сжаты ( например, при получении всех подкастов с PodcastsResource ):

Использование аннотации @Compress по методу ресурса

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
29
30
31
32
33
34
35
@Component
@Path("/podcasts")
public class PodcastsResource {
 
    @Autowired
    private PodcastService podcastService;
 
    ...........................
     
    /*
     * *********************************** READ ***********************************
     */
    /**
     * Returns all resources (podcasts) from the database
     *
     * @return
     * @throws IOException
     * @throws JsonMappingException
     * @throws JsonGenerationException
     * @throws AppException
     */
    @GET
    @Compress
    @Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
    public List<Podcast> getPodcasts(
            @QueryParam("orderByInsertionDate") String orderByInsertionDate,
            @QueryParam("numberDaysToLookBack") Integer numberDaysToLookBack)
            throws IOException, AppException {
        List<Podcast> podcasts = podcastService.getPodcasts(
                orderByInsertionDate, numberDaysToLookBack);
        return podcasts;
    }
     
    ...........................
}

2. Тестирование

2.1. SoapUI

Что ж, если вы тестируете с SOAPui, вы можете отправить следующий запрос к PodcastsResource .

Запрос:

Пример запроса

1
2
3
4
5
6
GET http://localhost:8888/demo-rest-jersey-spring/podcasts/?orderByInsertionDate=DESC HTTP/1.1
Accept-Encoding: gzip,deflate
Accept: application/json, application/xml
Host: localhost:8888
Connection: Keep-Alive
User-Agent: Apache-HttpClient/4.1.1 (java 1.5)

Отклик:

GZipped ответ json, автоматически разархивируется SOAPui

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
HTTP/1.1 200 OK
Content-Type: application/json
Content-Encoding: gzip
Content-Length: 409
Server: Jetty(9.0.7.v20131107)
 
[
   {
      "id": 2,
      "title": "Quarks & Co - zum Mitnehmen",
      "linkOnPodcastpedia": "http://www.podcastpedia.org/quarks",
      "feed": "http://podcast.wdr.de/quarks.xml",
      "description": "Quarks & Co: Das Wissenschaftsmagazin",
      "insertionDate": "2014-10-29T10:46:13.00+0100"
   },
    
   {
      "id": 1,
      "title": "- The Naked Scientists Podcast - Stripping Down Science",
      "feed": "feed_placeholder",
      "description": "The Naked Scientists flagship science show brings you a lighthearted look at the latest scientific breakthroughs, interviews with the world top scientists, answers to your science questions and science experiments to try at home.",
      "insertionDate": "2014-10-29T10:46:02.00+0100"
   }
]

SOAPui распознает заголовок Content-Type: gzip , который мы добавили в GZIPWriterInterceptor и автоматически распаковывает ответ и отображает его для чтения человеческим глазом.

Ну вот и все. Вы узнали, как Джерси позволяет сжимать ответы API REST.

Совет: Если вы действительно хотите научиться проектировать и реализовывать REST API в Java, прочитайте следующее руководство — Разработка и реализация REST API на Java с использованием Jersey и Spring

Ссылка: Как сжать ответы в Java REST API с помощью GZip и Jersey от нашего партнера по JCG Адриана Матеи в блоге Codingpedia.org .