Подтвердить что ты не робот

Как ограничить количество подключений Jetty?

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

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

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

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

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

4b9b3361

Ответ 1

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

}

Ответ 2

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

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

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

Ответ 3

Я не использовал Jetty для своего приложения. Однако используется 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>

Ответ 4

acceptQueueSize

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

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

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

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

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

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

Ответ 5

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