Как я могу ограничить количество соединений, которые примет Jetty?

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

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

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

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

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

5 ответов


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

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

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


Я закончил с решением, которое отслеживает количество запросов и отправляет 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);
    }

}

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

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

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>

acceptQueueSize

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

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

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

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

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

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


<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>