Как мне ограничить количество подключений, которые принимает Jetty? - PullRequest
17 голосов
/ 21 апреля 2011

Я использую Jetty 7.2.2 и хочу ограничить число подключений, которые он будет обрабатывать, так что, когда он достигнет предела (например, 5000), он начнет отказывать в подключениях.

К сожалению, все Connectors, кажется, просто идут вперед и принимают входящие соединения так быстро, как могут, и отправляют их в настроенный пул потоков.

Моя проблема в том, что я работаю в ограниченной среде, и у меня есть доступ только к дескрипторам файлов 8K. Если я получаю несколько подключений, я могу быстро исчерпать файловые дескрипторы и войти в противоречивое состояние.

Один из вариантов, который у меня есть, - вернуть HTTP 503 Service Unavailable, но для этого все равно требуется, чтобы я принял и ответил на соединение - и я бы где-то отслеживал количество входящих соединений, возможно, написав фильтр сервлета ,

Есть ли лучшее решение для этого?

Ответы [ 5 ]

12 голосов
/ 11 июля 2012

Пул потоков имеет связанную с ним очередь.По умолчанию оно не ограничено.Однако при создании пула потоков вы можете предоставить ограниченную очередь для его создания.Например:

Server server = new Server();
LinkedBlockingQueue<Runnable> queue = new LinkedBlockingQueue<Runnable>(maxQueueSize);
ExecutorThreadPool pool = new ExecutorThreadPool(minThreads, maxThreads, maxIdleTime, TimeUnit.MILLISECONDS, queue);
server.setThreadPool(pool);

Это, похоже, решило проблему для меня.В противном случае при неограниченной очереди на сервере не хватало файловых дескрипторов, поскольку он запускался под большой нагрузкой.

10 голосов
/ 06 декабря 2012

В итоге я выбрал решение, которое отслеживает количество запросов и отправляет 503, когда нагрузка слишком высокая. Это не идеально, и, как вы видите, мне пришлось добавить способ, позволяющий всегда пропускать запросы на продолжение, чтобы они не голодали. Хорошо работает для моих нужд:

public class MaxRequestsFilter implements Filter {

    private static Logger cat   = Logger.getLogger(MaxRequestsFilter.class.getName());

    private static final int DEFAULT_MAX_REQUESTS = 7000;
    private Semaphore requestPasses;

    @Override
    public void destroy() {
        cat.info("Destroying MaxRequestsFilter");
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {

        long start = System.currentTimeMillis();
        cat.debug("Filtering with MaxRequestsFilter, current passes are: " + requestPasses.availablePermits());
        boolean gotPass = requestPasses.tryAcquire();
        boolean resumed = ContinuationSupport.getContinuation(request).isResumed();
        try {
            if (gotPass || resumed ) {
                chain.doFilter(request, response);
            } else {
                ((HttpServletResponse) response).sendError(HttpServletResponse.SC_SERVICE_UNAVAILABLE);
            }
        } finally {
            if (gotPass) {
                requestPasses.release();
            }
        }
        cat.debug("Filter duration: " + (System.currentTimeMillis() - start) + " resumed is: " + resumed);
    }

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

        cat.info("Creating MaxRequestsFilter");

        int maxRequests = DEFAULT_MAX_REQUESTS;
        requestPasses = new Semaphore(maxRequests, true);
    }

}
5 голосов
/ 05 сентября 2011

acceptQueueSize

Если я правильно понимаю, это параметр TCP более низкого уровня, который контролирует количество входящих соединений, которые будут отслеживаться, когда серверное приложение действительно принимает () с меньшей скоростью, чем скорость входящих соединений. Смотрите второй аргумент для http://download.oracle.com/javase/6/docs/api/java/net/ServerSocket.html#ServerSocket(int,%20int)

Это что-то совершенно иное, чем количество запросов в очереди в Jetty QueuedThreadPool. Запросы, находящиеся в очереди, уже полностью подключены и ожидают, когда поток станет доступным в пуле, после чего их обработка может начаться.

У меня похожая проблема. У меня есть сервлет с процессором (почти нет ввода-вывода или ожидания, поэтому асинхронность не может помочь). Я могу легко ограничить максимальное количество потоков в пуле Jetty, так что издержки на переключение потоков останутся в стороне. Однако я не могу ограничить длину запросов в очереди. Это означает, что с ростом нагрузки соответственно увеличивается время отклика, а это не то, чего я хочу.

Я хочу, чтобы, если все потоки были заняты и число запросов в очереди достигало N, чтобы потом возвращать 503 или какой-либо другой код ошибки для всех дальнейших запросов, вместо того, чтобы постоянно увеличивать очередь.

Мне известно, что я могу ограничить количество одновременных запросов к серверу Jetty с помощью балансировщика нагрузки (например, haproxy), но можно ли это сделать только с Jetty?

P.S. После написания этого я обнаружил фильтр JetS DoS, и кажется, что он может быть настроен на отклонение входящих запросов с 503, если превышен предварительно настроенный уровень параллелизма: -)

5 голосов
/ 21 апреля 2011

Я не развернул Jetty для своего приложения.Однако использовал Jetty с некоторыми другими проектами с открытым исходным кодом для развертывания.Основываясь на этом опыте: есть конфигурация для соединителя, как показано ниже:

акцепторы: количество потоков, выделенных для приема входящих соединений.

acceptQueueSize: число запросов на соединение, которые могут быть поставлены в очередь дооперационная система начинает отправлять отказы.

http://wiki.eclipse.org/Jetty/Howto/Configure_Connectors

Вам необходимо добавить их в блок ниже в вашей конфигурации

<Call name="addConnector">
  <Arg>
      <New class="org.mortbay.jetty.nio.SelectChannelConnector">
        <Set name="port"><SystemProperty name="jetty.port" default="8080"/></Set>
        <Set name="maxIdleTime">30000</Set>
        <Set name="Acceptors">20</Set>
        <Set name="confidentialPort">8443</Set>
      </New>
  </Arg>
</Call>
3 голосов
/ 11 октября 2013
<Configure id="Server" class="org.eclipse.jetty.server.Server">
    <Set name="ThreadPool">
      <New class="org.eclipse.jetty.util.thread.QueuedThreadPool">
        <!-- specify a bounded queue -->
        <Arg>
           <New class="java.util.concurrent.ArrayBlockingQueue">
              <Arg type="int">6000</Arg>
           </New>
      </Arg>
        <Set name="minThreads">10</Set>
        <Set name="maxThreads">200</Set>
        <Set name="detailedDump">false</Set>
      </New>
    </Set>
</Configure>
...