Java-клиент, использующий сжатие OkHttpClient Gzip с помощью WebSphere Liberty

#java #gzip #okhttp #websphere-liberty

#java #gzip #OkHttp #WebSphere-liberty

Вопрос:

Мы используем WebSphere Liberty 21.0.0.x и Java 11. У нас есть главный сервер, и у нас есть несколько микросервисов, которые тайно передают данные на главный сервер. Пользователи получат доступ к пользовательскому интерфейсу через URL-адрес основного сервера и ничего не узнают о микросервисах под обложками. Сегодня мы написали код на Java с использованием java-библиотек OkHttp для выполнения запросов к микросервису. Эти запросы могут быть запросами GET или POST. В любом типе запроса есть данные тела сообщения (в форме json), и они могут быть довольно большими … отсюда и необходимость сжатия. Обратные ответы также могут быть довольно большими. Я хочу, чтобы сжатие gzip было до микросервиса и обратно.

Я включил сжатие в WebSphere Liberty, выполнив шаги, описанные здесь: https://openliberty.io/blog/2020/04/22/http-response-compression.html Кульминационный момент заключается в добавлении некоторого xml в server.xml . Это довольно просто. Вот основной фрагмент из моего server.xml файл:

 <httpEndpoint host="*" id="defaultHttpEndpoint" httpPort="9080" httpsPort="9443">
        <compression serverPreferredAlgorithm="gzip">
            <types> application/*</types>
            <types>-text/plain</types>
        </compression>
    </httpEndpoint> 
 

Я понимаю, что это все, что мне нужно сделать на стороне сервера, и любая кодировка содержимого, с которой клиент обращается, будет учитываться сервером.

Вот клиентский код на java, в котором я пытаюсь опубликовать сообщение с использованием сжатия gzip:

 private static final OkHttpClient client = new OkHttpClient.Builder()
              .addInterceptor(new CompressionInterceptor())
              .readTimeout(5000, TimeUnit.MILLISECONDS)
              .writeTimeout(10000, TimeUnit.MILLISECONDS)
              .build();

// later in the code
RequestBody jsonRequestBody = RequestBody.create(data, MEDIA_TYPE_JSON);
Request request = new Request.Builder()
    .url(urlString)
    .addHeader("Accept", "application/json")
    .post(jsonRequestBody)
    .build();

Call call = client.newCall(request);
Response  response = call.execute();            
if (response.isSuccessful()) {
    String responseString = response.body().string();
}
...
 

Вот код запроса GET:

 String url = urlBuilder.build().toString();
Request request = new Request.Builder().url(url).addHeader("Accept", "application/json").build();
Call call = client.newCall(request);
Response response = call.execute();
if (response.isSuccessful()) {
    String someResponse = response.body().string();
}
...
 

Вот класс перехватчика, который я настроил для создания экземпляра OkHttpClient:

 public class CompressionInterceptor implements Interceptor {
    
    private static final String CONTENT_ENCODING = "Content-Encoding";
    private static final String GZIP = "gzip";

    @Override
    public Response intercept(Chain chain) throws IOException {
        Request request = chain.request();
        if (request.body() == null || request.header(CONTENT_ENCODING) != null) {
            return chain.proceed(request);
        }

        Request compressedRequest = request.newBuilder().header(CONTENT_ENCODING, GZIP)
                .method(request.method(), gzip(request.body())).build();
        return chain.proceed(compressedRequest);
    }

    private RequestBody gzip(final RequestBody body) {
        return new RequestBody() {
            @Override
            public MediaType contentType() {
                return body.contentType();
            }

            @Override
            public long contentLength() {
                return -1; // Allow any length
            }

            @Override
            public void writeTo(BufferedSink sink) throws IOException {
                BufferedSink gzipSink = Okio.buffer(new GzipSink(sink));
                body.writeTo(gzipSink);
                gzipSink.close();
            }

        };
    }
}
 

У меня есть пара вопросов.
a.) Я не уверен, что вышеприведенное действительно работает так, как задумано. Могу ли я сказать что-нибудь, кроме wire shark? Когда я тестирую с использованием REST-клиента, такого как Postman, я вижу, что в ответе сервера gzip является частью заголовков. Однако, со стороны кода, я не могу на 100% сказать, что он отправляет данные в сжатом виде.
б.) Я не уверен, что у меня система правильно настроена / закодирована для сжатия в обоих направлениях. т.е. я не уверен, что и запрос, и ответ действительно сжаты.

Комментарии:

1. Для (a) я лично обнаружил, что tcptunnel полезен в этих ситуациях. ( github.com/vakuum/tcptunnel ) В основном установите tcptunnel для прослушивания порта X и перенаправления на порт Y (который прослушивает сервер), и он будет печатать трафик между ними. Затем вам нужно изменить свой клиент, чтобы он подключался к порту X и выполнял запросы. Это покажет вам, правильно ли добавлены заголовки к запросу и ответу. Я не знаю о (b), но кто-то, более знакомый с функцией сжатия GZIP в Liberty, должен быть в состоянии помочь.

2. Конфигурация Liberty и код приложения, которые вы здесь разместили, выглядят правильно. Вы против использования Wireshark? Было бы довольно просто (с помощью Wireshark) проверить как a), так и b), выполнив поиск поля типа Content-encoded entity body (gzip) в запросах и ответах.

3. Я не против ни того, ни другого программного обеспечения. Я только что обнаружил, что анализаторы пакетов собирают так много трафика (даже когда вы пытаетесь захватить небольшое временное окно), что становится очень сложно. Достаточно справедливо, если это единственный и лучший способ.

4. Добавьте NetworkInterceptor в свое приложение, чтобы точно видеть, что отправляется по проводам.

5. @Юрий Шимке — Вы имеете в виду другой перехватчик OkHttp или фильтр Java? Можете ли вы предоставить некоторые подробности? Спасибо!