cognito - El cliente S3 Java falla mucho con el "cuerpo de mensaje delimitado por el final prematuro de contenido-longitud" o el "socket cerrado por java.net.SocketException"
spring s3 (6)
Tengo una aplicación que trabaja mucho en S3, principalmente descargando archivos de ella. Estoy viendo muchos de estos tipos de errores y me gustaría saber si esto es algo en mi código o si el servicio no es confiable como este.
El código que estoy usando para leer de la secuencia de objetos S3 es el siguiente:
public static final void write(InputStream stream, OutputStream output) {
byte[] buffer = new byte[1024];
int read = -1;
try {
while ((read = stream.read(buffer)) != -1) {
output.write(buffer, 0, read);
}
stream.close();
output.flush();
output.close();
} catch (IOException e) {
throw new RuntimeException(e);
}
}
Este OutputStream es un nuevo BufferedOutputStream (nuevo FileOutputStream (archivo)) . Estoy usando la última versión del cliente Amazon S3 Java y esta llamada se reintenta cuatro veces antes de darme por vencida. Entonces, después de probar esto por 4 veces, aún falla.
Cualquier sugerencia o consejo sobre cómo podría mejorar esto se agradece.
Intente usar wireshark para ver qué sucede en el cable cuando esto suceda.
Intenta reemplazar temporalmente S3 con tu propio servidor web y verifica si el problema persiste. Si lo hace, es tu código y no S3.
El hecho de que sea aleatorio sugiere problemas de red entre su host y algunos de los hosts S3.
Miraría muy de cerca el equipo de red más cercano a su aplicación cliente. Este problema huele a que algunos dispositivos de red eliminan paquetes entre usted y el servicio. Mire para ver si hubo un punto de partida cuando ocurrió el problema por primera vez. ¿Hubo algún cambio, como una actualización de firmware en un enrutador o el reemplazo de un conmutador en ese momento?
Verifique su uso de ancho de banda contra la cantidad comprada de su ISP. ¿Hay momentos del día en los que te estás acercando a ese límite? ¿Puedes obtener gráficos de tu uso de ancho de banda? Vea si las terminaciones prematuras pueden correlacionarse con el uso de gran ancho de banda, particularmente si se acerca a algún límite conocido. ¿El problema parece abarcar archivos más pequeños y archivos grandes solo cuando están casi terminados de descargarse? Comprar más ancho de banda de su ISP puede solucionar el problema.
También S3 podría cerrar conexiones lentas según mi experiencia.
Solo logré superar un problema muy similar. En mi caso, la excepción que recibía era idéntica; sucedió con archivos más grandes pero no con archivos pequeños, y nunca sucedió en absoluto al pasar por el depurador.
La causa principal del problema era que el objeto AmazonS3Client estaba recibiendo basura recolectada en el medio de la descarga, lo que provocó que la conexión de red se rompiera. Esto sucedió porque estaba construyendo un nuevo objeto AmazonS3Client con cada llamada para cargar un archivo, mientras que el caso de uso preferido es crear un objeto cliente duradero que sobreviva en llamadas, o al menos se garantice que esté disponible durante la totalidad del proceso. descargar. Entonces, el remedio simple es asegurarse de que se mantenga una referencia a AmazonS3Client para que no se obtenga GC''d.
Un enlace en los foros de AWS que me ayudó está aquí: https://forums.aws.amazon.com/thread.jspa?threadID=83326
La red está cerrando la conexión, antes de que el cliente obtenga todos los datos, por una razón u otra, eso es lo que está sucediendo.
Parte de cualquier solicitud HTTP es la longitud del contenido, tu código está recibiendo el encabezado, diciendo "amigo", aquí están los datos, y esto es gran parte de eso ... y luego la conexión está cayendo antes de que el cliente haya leído todos los datos ... entonces su bombardeo con la excepción.
Me gustaría ver la configuración de tiempo de espera de la conexión OS / NETWORK / JVM (aunque JVM generalmente hereda del sistema operativo en esta situación). La clave es descubrir qué parte de la red está causando el problema. ¿Es su configuración de nivel de equipo que dice, no, que no va a esperar más para los paquetes ... es que está utilizando una lectura no bloqueante, que tiene una configuración de tiempo de espera en su código, donde está diciendo, hey, no ha llegado cualquier información del servidor desde más tiempo de lo que se supone que debo esperar, así que voy a soltar la conexión y la excepción. etc etc, etc.
La mejor apuesta es hacer un seguimiento de bajo nivel del tráfico de paquetes y rastrear hacia atrás, para ver dónde se produce la caída de la conexión, o ver si puede aumentar los tiempos de espera en cosas que puede controlar, como su software y OS / JVM.
En primer lugar, su código está funcionando completamente normalmente si (y solo si) sufre problemas de conectividad entre usted y Amazon S3. Como señala Michael Slade, se aplica el consejo de depuración de nivel de conexión estándar.
En cuanto a su código fuente real, observo algunos olores de código que debe tener en cuenta. Anotándolos directamente en la fuente:
public static final void write(InputStream stream, OutputStream output) {
byte[] buffer = new byte[1024]; // !! Abstract 1024 into a constant to make
// this easier to configure and understand.
int read = -1;
try {
while ((read = stream.read(buffer)) != -1) {
output.write(buffer, 0, read);
}
stream.close(); // !! Unexpected side effects: closing of your passed in
// InputStream. This may have unexpected results if your
// stream type supports reset, and currently carries no
// visible documentation.
output.flush(); // !! Violation of RAII. Refactor this into a finally block,
output.close(); // a la Reference 1 (below).
} catch (IOException e) {
throw new RuntimeException(e); // !! Possibly indicative of an outer
// try-catch block for RuntimeException.
// Consider keeping this as IOException.
}
}
( Referencia 1 )
De lo contrario, el código en sí parece estar bien. Las excepciones IO deben ser situaciones esperadas en situaciones en las que se conecta a un host remoto voluble, y lo mejor que puede hacer es redactar una política sensata para almacenar en caché y volver a conectar en estos escenarios.