18

我正在运行 Jetty 7.2.2 并希望限制它将处理的连接数,这样当它达到限制(例如 5000)时,它将开始拒绝连接。

不幸的是,所有这些Connectors似乎都继续并尽可能快地接受传入的连接并将它们分派到配置的线程池中。

我的问题是我在受限环境中运行,我只能访问 8K 文件描述符。如果我有一堆连接进来,我会很快用完文件描述符并进入不一致的状态。

我有一个选择是返回一个 HTTP 503 Service Unavailable,但这仍然需要我接受并响应连接 - 我可能会通过编写一个 servlet 过滤器来跟踪某个地方的传入连接数。

有没有更好的解决方案?

4

5 回答 5

11

线程池有一个与之关联的队列。默认情况下,它是无界的。但是,在创建线程池时,您可以提供一个有界队列作为其基础。例如:

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

这似乎已经为我解决了这个问题。否则,对于无界队列,服务器在重负载下启动时会耗尽文件句柄。

于 2012-07-11T16:52:25.187 回答
10

我最终选择了一个解决方案,该解决方案跟踪请求数量并在负载过高时发送 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);
    }

}
于 2012-12-06T18:10:04.583 回答
5

接受队列大小

如果我理解正确,这是一个较低级别的 TCP 设置,它控制当服务器应用程序以低于传入连接的速率时接受()时将跟踪的传入连接数。请参阅http://download.oracle.com/javase/6/docs/api/java/net/ServerSocket.html#ServerSocket(int,%20int)的第二个参数

这与 Jetty QueuedThreadPool 中排队的请求数量完全不同。在那里排队的请求已经完全连接,并且正在等待线程池中可用的线程,之后它们的处理就可以开始了。

我有一个类似的问题。我有一个受 CPU 限制的 servlet(几乎没有 I/O 或等待,所以 async 无济于事)。我可以轻松限制 Jetty 池中的最大线程数,从而避免线程切换开销。但是,我似乎无法限制排队请求的长度。这意味着随着负载的增长,响应时间分别增长,这不是我想要的。

我希望如果所有线程都忙并且排队的请求数达到 N,然后为所有进一步的请求返回 503 或其他错误代码,而不是永远增加队列。

我知道我可以通过使用负载平衡器(例如 haproxy)来限制对码头服务器的同时请求数量,但是可以单独使用 Jetty 来完成吗?

PS 在写完这篇文章后,我发现了 Jetty DoS 过滤器,如果超过了预配置的并发级别,它似乎可以配置为拒绝传入请求 503 :-)

于 2011-09-05T09:30:46.090 回答
5

我还没有为我的应用程序部署 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>
于 2011-04-20T23:59:17.540 回答
3
<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>
于 2013-10-11T06:27:56.120 回答