我正在运行Jetty 7.2.2并希望限制它将处理的连接数,这样当它达到限制(例如5000)时,它将开始拒绝连接。
不幸的是,所有Connectors
似乎只是继续并尽可能快地接受传入连接并将它们分派到配置的线程池。
我的问题是我在受限制的环境中运行,而且我只能访问8K文件描述符。如果我收到一堆连接,我可以快速耗尽文件描述符并进入不一致状态。
我有一个选择是返回一个HTTP 503 Service Unavailable
,但这仍然要求我接受并响应连接 - 我会跟踪某处传入连接的数量,也许是通过写一个servlet过滤器。
有更好的解决方案吗?
答案 0 :(得分:12)
线程池有一个与之关联的队列。默认情况下,它是无限的。但是,在创建线程池时,您可以提供一个基于它的有界队列。例如:
Server server = new Server();
LinkedBlockingQueue<Runnable> queue = new LinkedBlockingQueue<Runnable>(maxQueueSize);
ExecutorThreadPool pool = new ExecutorThreadPool(minThreads, maxThreads, maxIdleTime, TimeUnit.MILLISECONDS, queue);
server.setThreadPool(pool);
这似乎解决了我的问题。否则,对于无界队列,服务器在重负载下启动时会用完文件句柄。
答案 1 :(得分: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);
}
}
答案 2 :(得分: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>
答案 3 :(得分:5)
acceptQueueSize
如果我理解正确,这是一个较低级别的TCP设置,它控制当服务器应用程序接受()时将跟踪的传入连接数,其速率低于传入连接时的速率。请参阅http://download.oracle.com/javase/6/docs/api/java/net/ServerSocket.html#ServerSocket(int,%20int)
的第二个参数这与Jetty QueuedThreadPool中排队的请求数完全不同。排队的请求已经完全连接,正在等待一个线程在池中可用,之后可以开始处理。
我有类似的问题。我有一个CPU绑定的servlet(几乎没有I / O或等待,所以异步无法帮助)。我可以轻松地限制Jetty池中的最大线程数,以便保持线程切换开销。但是,我似乎无法限制排队请求的长度。这意味着随着负载的增加,响应时间分别增长,这不是我想要的。
我希望如果所有线程都忙,并且排队的请求数达到N,则返回503或其他所有进一步请求的错误代码,而不是永远增长队列。
我知道我可以通过使用负载均衡器(例如haproxy)来限制同时向jetty服务器发出请求的数量,但是可以单独使用Jetty吗?
P.S。 写完之后,我发现了Jetty DoS过滤器,如果超出预配置的并发级别,它似乎可以配置为拒绝传入的请求: - )
答案 4 :(得分: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>