2011-04-20 8 views
12

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.¿Cómo limito el número de conexiones que Jetty aceptará?

Desafortunadamente, todos los Connectors parecen seguir adelante y aceptar las conexiones entrantes tan rápido como puedan y enviarlas al grupo de subprocesos configurado.

Mi problema es que me estoy ejecutando en un entorno restringido, y solo tengo acceso a los descriptores de archivos 8K. Si recibo un montón de conexiones, puedo rápidamente quedarme sin descriptores de archivos y entrar en un estado incoherente.

Una opción que tengo es devolver un HTTP 503 Service Unavailable, pero eso todavía requiere que yo acepte y responda a la conexión, y habría hecho un seguimiento del número de conexiones entrantes en algún lugar, quizás escribiendo un filtro de servlet.

¿Hay una mejor solución para esto?

Respuesta

9

Terminé yendo con una solución que realiza un seguimiento del número de solicitudes y envía un 503 cuando la carga es demasiado alta. No es ideal, y como pueden ver, tuve que agregar una forma de permitir siempre las solicitudes de continuación para que no pasaran 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); 
    } 

} 
5

No he implementado Jetty para mi aplicación. Sin embargo, se utilizó Jetty con algunos otros proyectos de código abierto para la implementación. Basado en esa experiencia: Hay una configuración para el conector de la siguiente manera:

aceptadores: el número de hilos 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

es necesario agregar a continuación el bloque en su 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> 
+1

El problema es que el 'Acceptors' acaba de volcar las conexiones en una cola tan rápido como sea posible y luego ir a buscar más, por lo que nunca se alcanza el límite SO . – BigBen

4

acceptQueueSize

Si he entendido bien, se trata de un ajuste de TCP nivel inferior, que controla la cantidad de conexiones entrantes que se rastrearán cuando la aplicación del servidor acepte() a un ritmo más lento que la tasa si es inco conexiones ming. Vea el segundo argumento en http://download.oracle.com/javase/6/docs/api/java/net/ServerSocket.html#ServerSocket(int, int)

Esto es algo completamente diferente del número de solicitudes en cola en Jetty QueuedThreadPool. Las solicitudes puestas en cola allí ya están conectadas por completo y esperan que un hilo esté disponible en el grupo, después de lo cual puede comenzar su procesamiento.

Tengo un problema similar. Tengo un servlet vinculado a CPU (casi sin E/S o esperando, por lo que async no puede ayudar). Puedo limitar fácilmente la cantidad máxima de subprocesos en el grupo Jetty para que la sobrecarga de conmutación de subprocesos se mantenga a raya. No obstante, parece que no puedo limitar la duración de las solicitudes en cola. Esto significa que a medida que la carga crece, los tiempos de respuesta crecen respectivamente, que no es lo que quiero.

Deseo si todos los hilos están ocupados y el número de solicitudes en cola llega a N, luego devolver 503 o algún otro código de error para todas las solicitudes adicionales, en lugar de hacer crecer la cola para siempre.

Soy consciente de que puedo limitar el número de solicitudes simultáneas al servidor Jetty utilizando un equilibrador de carga (por ejemplo, haproxy), pero ¿se puede hacer con Jetty solo?

P.S. Después de escribir esto, descubrí el filtro Jetty DoS y parece que se puede configurar para rechazar solicitudes entrantes con 503 si se excede un nivel de concurrencia preconfigurado :-)

12

El grupo de subprocesos tiene una cola asociada. Por defecto, no tiene límites. Sin embargo, al crear un grupo de subprocesos, puede proporcionar una cola acotada para basarlo. 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 identificadores de archivos cuando se inició bajo una gran carga.

2
<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> 
+0

Puede llamar a setMaxQueued en QueuedThreadPool, creará una instancia de ArrayBlockingQueue por sí mismo cuando se establezca este parámetro. –

Cuestiones relacionadas