javajettyembedded-jetty

How do I limit the number of connections Jetty will accept?


I'm running Jetty 7.2.2 and want to limit the number of connections it will handle, such that when it reaches a limit (eg 5000), it will start refusing connections.

Unfortunately, all the Connectors appear to just go ahead and accept incoming connections as fast as they can and dispatch them to the configured thread pool.

My problem is that I'm running in a constrained environment, and I only have access to 8K file descriptors. If I get a bunch of connections coming in I can quickly run out of file descriptors and get into an inconsistent state.

One option I have is to return an HTTP 503 Service Unavailable, but that still requires me to accept and respond to the connection - and I'd have keep track of the number of incoming connections somewhere, perhaps by writing a servlet filter.

Is there a better solution to this?


Solution

  • I ended up going with a solution which keeps track of the number of requests and sends a 503 when the load is too high. It's not ideal, and as you can see I had to add a way to always let continuation requests through so they didn't get starved. Works well for my needs:

    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);
        }
    
    }