Apache буферизует ответы Tomcat и закрывает соединение с Tomcat, когда размер запроса превышает 10397232 байта. - PullRequest
0 голосов
/ 16 января 2019

Я развернул приложение веб-службы в виде контейнера Docker на основе tomee: 8-jre-7.0.4-plume, который находится за экземпляром apache httpd: 2.4, работающим в другом контейнере Docker, работающем на той же машине.

Проблемы заключаются в том, что a) apache буферизует ответ, что создает значительную задержку, и b) если объем данных слишком велик (10397232 байта), apache закрывает соединение с TomEE, который сообщает org.apache.catalina.connector.ClientAbortException: java.net.SocketTimeoutException.Я думаю, что это соответствует сообщению, которое я вижу в apache error_log (LogLevel trace6) output_filter: сброс из-за THRESHOLD_MAX_BUFFER.Это происходит независимо от того, использую ли я https или http.

. Если я отправляю запросы непосредственно в TomEE, буферизация ответов не происходит и при больших запросах не возникает ошибок.

Я включил некоторый код, который может воспроизвести проблему.Вы заметите, что я в основном передаю ввод запроса в 'cat', а затем отправляю вывод его в другой 'cat'.затем скопируйте вывод окончательного вывода 'cat' в поток вывода ответа.В «реальном» приложении я не использую cat и делаю что-то гораздо более интересное, но этот пример иллюстрирует суть проблемы.Кроме того, если я изменяю свой код, чтобы записать вывод окончательного 'cat' в локальный файл, а затем просто скопировать содержимое этого файла в поток вывода ответа, то проблемы исчезнут, однако это приводит к тому же видузадержка как буферизация всего ответа.

Я пробовал несколько изменений конфигурации в apache httpd.conf

<IfModule proxy_ajp_module>
    ProxyPass "/" "http://localhost:8080/" timeout=18000
    TimeOut 18000
</IfModule>

Я пробовал различные комбинации из следующих:

    ProxyPass "/" "http://localhost:82/" timeout=18000 flushpackets=on receivebuffersize=8192 iobuffersize=8192

Я также попытался явно установить ряд параметров конфигурации TomEE / Tomcat, как указано в документации.В настоящее время я не использую протокол ajp.

Единственное изменение server.xml, которое в данный момент выполняется:

<Connector port="8080" allowTrace="true" ajpFlush="false" maxPostSize="-1" maxSavePostSize="-1"  proxyName="mycustomhost.com" protocol="HTTP/1.1"

Я включил здесь типичный код веб-службы:

package com.spike

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicBoolean;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.ws.rs.POST;
import javax.ws.rs.Path;
import javax.ws.rs.core.Context;

@Path("test")
public class MinTestingResource {
    private final ExecutorService execSvc = Executors.newFixedThreadPool(10);
    @POST
    public void post(
            @Context HttpServletRequest request,
            @Context HttpServletResponse response) throws IOException, InterruptedException, ExecutionException {
        InputStream in = request.getInputStream();
        OutputStream out = response.getOutputStream();
        convertStream(in, out);
    }
    private void convertStream(InputStream in, OutputStream out) throws ExecutionException, IOException, InterruptedException {
        Process p1 = new ProcessBuilder("cat").start();
        Process p2 = new ProcessBuilder("cat").start();
        copyInThread("in-p1", in, p1.getOutputStream());
        copyInThread("p1-p2", p1.getInputStream(), p2.getOutputStream());
        copy("p2-out", p2.getInputStream(), out);
    }
    private Future<Long> copyInThread(String nameToLog, InputStream in, OutputStream out) {
        AtomicBoolean copierStarted = new AtomicBoolean(false);
        final Future<Long> future = execSvc.submit(() -> {copierStarted.set(true); return copy(nameToLog, in, out);});
        waitForStart(nameToLog, copierStarted);
        return future;
    }
    private long copy(String nameToLog, InputStream in, OutputStream out) throws IOException {
        long copied = 0;
        try {
            byte[] buf = new byte[8192];
            for (int bytesRead = 0; (bytesRead = in.read(buf)) > 0;) {
                out.write(buf, 0, bytesRead);
                copied += bytesRead;
            }
            return copied;
        } finally {
            out.close();
        }
    }
    private void waitForStart(String nameToLog, AtomicBoolean copierStarted) {
        pause(5);
        while (!copierStarted.get()) {
            pause(5);
        }
    }
    private void pause(long millis) {
        try {
            Thread.sleep(millis);
        } catch (InterruptedException ignore) {
        }
    }
}

Я обнаружил следующую проблему в Apache bugzilla Ошибка 61616 - mod_proxy_connect: остановка и потеря соединения при двунаправленном трафике , но я не уверен, что это та же проблема, и это не такПонятно, что решение доступно в виде контейнера Docker.

...