Tengo una máquina FreeBSD con cárceles, dos en particular, una que ejecuta nginx y otra que ejecuta un programa Java que acepta solicitudes a través de Jetty (modo integrado).
Jetty recibe más de 500 solicitudes por segundo constantemente y últimamente ha habido un problema por el cual constantemente tengo más de 500 solicitudes por segundo.60.000conexiones en el estado LAST_ACK entre nginx y jetty.
Distribución de todas las conexiones (incluye algunos otros servicios, particularmente php-fpm)
root@host:/root # netstat -an > conns.txt
root@host:/root # cat conns.txt | awk '{print $6}' | sort | uniq -c | sort -n
18 LISTEN
112 CLOSING
485 ESTABLISHED
650 FIN_WAIT_2
1425 FIN_WAIT_1
3301 TIME_WAIT
64215 LAST_ACK
Distribución de nginx -> conexiones de embarcadero
root@host:/root # cat conns.txt | grep '10.10.1.57' | awk '{print $6}' | sort | uniq -c | sort -n
1
3 CLOSE_WAIT
3 LISTEN
18 FIN_WAIT_2
125 ESTABLISHED
64193 LAST_ACK
Preferiría que cada solicitud cierre completamente la conexión. Las solicitudes de los clientes tienen una diferencia de aproximadamente 10 minutos entre sí, por lo que las conexiones deben cerrarse.
Algunas de las conexiones,
tcp4 0 0 10.10.1.50.46809 10.10.1.57.9050 LAST_ACK
tcp4 0 0 10.10.1.50.46805 10.10.1.57.9050 LAST_ACK
tcp4 0 0 10.10.1.50.46797 10.10.1.57.9050 LAST_ACK
tcp4 0 0 10.10.1.50.46794 10.10.1.57.9050 LAST_ACK
tcp4 0 0 10.10.1.50.46790 10.10.1.57.9050 LAST_ACK
tcp4 0 0 10.10.1.50.46789 10.10.1.57.9050 LAST_ACK
tcp4 0 0 10.10.1.50.46771 10.10.1.57.9050 LAST_ACK
etc..
- En el extremo de Jetty lo configuré
maxIdleTime
en 2000; antes de esto, todas las conexiones estaban activadas,ESTABLISHED
pero ahora lo están.LAST_ACK
- En el extremo de Jetty he puesto
Connection: close
(es decirresponse.setHeader(HttpHeaders.CONNECTION, HttpHeaderValues.CLOSE);
) - Jetty nunca reporta muchas conexiones abiertas, siempre muy pocas.
- PF/IPFW no se utiliza actualmente
- nginx -
reset_timedout_connection
está activado
No puedo entender cómo hacer que nginx o jetty cierren la conexión por la fuerza. ¿Es esto simplemente algo que debe arreglarse en Jetty para que cierre completamente el socket una vez finalizada la solicitud?
Muchas gracias por adelantado
EDITAR:Olvidé mi configuración de nginx para la configuración del proxy.
proxy_pass http://10.10.1.57:9050;
proxy_set_header HTTP_X_GEOIP $http_x_geoip;
proxy_set_header GEOIP_COUNTRY_CODE $geoip_country_code;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header Host $http_host;
proxy_set_header Connection "";
proxy_http_version 1.1;
EDITAR2:Obligar a Jetty a cerrar la conexión request.getConnection().getEndPoint().close()
no hace nada; es obvio que la conexión SE ESTÁ cerrando (como está en LAST_ACK
), pero ¿por qué no se supera esto? ¿Nginx mantiene la conexión abierta con el backend por algún motivo?
Respuesta1
Bueno, finalmente pude convencer a Jetty para que jugara bien.
Solo para resumir, así es como se ven mis conexiones ahora, en toda la máquina:
24 LAST_ACK
36 CLOSING
117 FIN_WAIT_2
175 ESTABLISHED
351 FIN_WAIT_1
4725 TIME_WAIT
Y entre nginx y Jetty
1 FIN_WAIT_2
3 LISTEN
14 ESTABLISHED
Ya he estado cerrando toda la conexión y EndPoint
(llamando close()
a EndPoint
cerrar el subyacente SocketChannel
) usando este método conveniente:
private void finishRequest(String message, Request baseRequest, HttpServletResponse response) throws IOException {
ByteArrayISO8859Writer writer = new ByteArrayISO8859Writer(1500);
writer.write(message);
writer.flush();
// set the content length
response.setContentLength(writer.size());
// write the response
OutputStream outputStream = response.getOutputStream();
writer.writeTo(outputStream);
// close the streams
outputStream.close();
writer.close();
baseRequest.getConnection().getEndPoint().close();
}
(Solo envío una línea como máximo al cliente, por lo que no se necesita nada sofisticado)
Sin embargo, esto aún resultó en llenar todo el servidor con LAST_ACK... Lo que hizo que finalmente desaparecieran fue habilitar SO_LINGER
(y hacer que cerrara el socket inmediatamente, sin tiempo de espera)
connector.setSoLingerTime(0);