java - ¿Cómo limito el número de conexiones que Jetty aceptará?
embedded-jetty (5)
acceptQueueSize
Si entiendo correctamente, esta es una configuración de TCP de nivel inferior, que controla el número de conexiones entrantes que se rastrearán cuando la aplicación del servidor acepte () a una velocidad más lenta que la de las conexiones entrantes. Consulte el segundo argumento de http://download.oracle.com/javase/6/docs/api/java/net/ServerSocket.html#ServerSocket(int,%20int)
Esto es algo completamente diferente de la cantidad de solicitudes en cola en el Jetty QueuedThreadPool. Las solicitudes en cola allí ya están completamente conectadas y están esperando que un subproceso esté disponible en el grupo, después de lo cual puede comenzar su procesamiento.
Tengo un problema similar. Tengo un servlet vinculado a la CPU (casi sin E / S o en espera, por lo que async no puede ayudar). Puedo limitar fácilmente el número máximo de subprocesos en el grupo Jetty para que la sobrecarga de cambio de subprocesos se mantenga a raya. Sin embargo, parece que no puedo limitar la longitud de las solicitudes en cola. Esto significa que a medida que crece la carga, los tiempos de respuesta aumentan respectivamente, que no es lo que quiero.
Quiero que todos los subprocesos estén ocupados y la cantidad de solicitudes en cola llegue a N, luego devolver 503 o algún otro código de error para todas las demás solicitudes, en lugar de aumentar la cola para siempre.
Soy consciente de que puedo limitar el número de solicitudes simultáneas al servidor jetty mediante el uso de un equilibrador de carga (por ejemplo, haproxy), pero ¿se puede hacer solo con Jetty?
PS Después de escribir esto, descubrí el filtro Jetty DoS, y parece que se puede configurar para rechazar las solicitudes entrantes con 503 si se excede un nivel de concurrencia preconfigurado :-)
Estoy ejecutando Jetty 7.2.2 y quiero limitar el número de conexiones que manejará, de modo que cuando alcance un límite (por ejemplo, 5000), comenzará a rechazar las conexiones.
Desafortunadamente, todos los Connectors
parecen seguir adelante y aceptar las conexiones entrantes lo más rápido posible y enviarlas al grupo de subprocesos configurado.
Mi problema es que estoy ejecutando en un entorno restringido y solo tengo acceso a los descriptores de archivos de 8K. Si entro en un montón de conexiones, rápidamente me quedaré sin descriptores de archivos y entraré en un estado inconsistente.
Una de las opciones que tengo es devolver un servicio HTTP 503 Service Unavailable
, pero eso aún requiere que acepte y responda a la conexión, y debo hacer un seguimiento del número de conexiones entrantes en algún lugar, tal vez escribiendo un filtro de servlet.
¿Hay una solución mejor para esto?
El grupo de hilos tiene una cola asociada a él. Por defecto, es ilimitado. Sin embargo, al crear un grupo de subprocesos, puede proporcionar una cola limitada en la que basarse. Por ejemplo:
Server server = new Server();
LinkedBlockingQueue<Runnable> queue = new LinkedBlockingQueue<Runnable>(maxQueueSize);
ExecutorThreadPool pool = new ExecutorThreadPool(minThreads, maxThreads, maxIdleTime, TimeUnit.MILLISECONDS, queue);
server.setThreadPool(pool);
Esto parece haber resuelto el problema para mí. De lo contrario, con la cola ilimitada, el servidor se quedó sin manejadores de archivos cuando se inició bajo una carga pesada.
No he desplegado Jetty para mi aplicación. Sin embargo, usé Jetty con algunos otros proyectos de código abierto para la implementación. De acuerdo con esa experiencia: hay una configuración para el conector de la siguiente manera:
aceptores: el número de subproceso dedicado a aceptar conexiones entrantes.
acceptQueueSize: número de solicitudes de conexión que pueden ponerse en cola antes de que el sistema operativo comience a enviar rechazos.
http://wiki.eclipse.org/Jetty/Howto/Configure_Connectors
Necesitas agregarlos al siguiente bloque en tu configuración.
<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>
Terminé yendo con una solución que hace un seguimiento del número de solicitudes y envía un 503 cuando la carga es demasiado alta. No es lo ideal, y como pueden ver, tuve que agregar una forma de dejar siempre las solicitudes de continuación para que no se murieran de hambre. Funciona bien para mis necesidades:
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);
}
}
<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>