Cómo mejorar el rendimiento de la iteración más de 130 artículos de subirlos a AWS S3

fsakiyama:

Tengo que iterate más de 130 objetos de transferencia de datos, y cada vez que va a generar un JSON para ser subido a AWS S3.

Sin mejoras, se tarda alrededor de 90 segundos la completa todo el proceso. He intentado utilizar lamba y no usar Lamba, mismos resultados para ambos.

for(AbstractDTO dto: dtos) {
    try {
        processDTO(dealerCode, yearPeriod, monthPeriod, dto);
    } catch (FileAlreadyExistsInS3Exception e) {
        failedToUploadDTOs.add(e.getLocalizedMessage() + ": " + dto.fileName() + ".json");
    }
}
dtos.stream().forEach(dto -> {
    try {
        processDTO(dealerCode, yearPeriod, monthPeriod, dto);
    } catch (FileAlreadyExistsInS3Exception e) {
        failedToUploadDTOs.add(e.getLocalizedMessage() + ": " + dto.fileName() + ".json");
    }
});

Después de algunas investigaciones, llegué a la conclusión de que el método processDTO lleva alrededor 0.650ms por artículo a plazo.

Mi primer intento fue utilizar corrientes paralelas , y los resultados fueron bastante buenos, teniendo alrededor de 15 segundos para completar todo el proceso:

dtos.parallelStream().forEach(dto -> {
    try {
        processDTO(dealerCode, yearPeriod, monthPeriod, dto);
    } catch (FileAlreadyExistsInS3Exception e) {
        failedToUploadDTOs.add(e.getLocalizedMessage() + ": " + dto.fileName() + ".json");
    }
});

Pero todavía tengo que disminuir ese tiempo. He investigado sobre la mejora de corrientes paralelas, y descubrí la ForkJoinPool truco:

ForkJoinPool forkJoinPool = new ForkJoinPool(PARALLELISM_NUMBER);
forkJoinPool.submit(() ->
dtos.parallelStream().forEach(dto -> {
    try {
        processDTO(dealerCode, yearPeriod, monthPeriod, dto);
    } catch (FileAlreadyExistsInS3Exception e) {
        failedToUploadDTOs.add(e.getLocalizedMessage() + ": " + dto.fileName() + ".json");
    }
})).get();
forkJoinPool.shutdown();

Desafortunadamente, los resultados fueron un poco confuso para mí.

  • Cuando PARALLELISM_NUMBER es 8, que toma alrededor de 13 segundos para completar todo el proceso. No es una gran mejora.
  • Cuando PARALLELISM_NUMBER es 16, se tarda alrededor de 8 segundos para completar todo el proceso.
  • Cuando PARALLELISM_NUMBER es de 32, se tarda alrededor de 5 segundos para completar todo el proceso.

Todas las pruebas se realizaron utilizando las solicitudes de cartero, llamando al método controlador que terminará-up iterando las 130 artículos

Estoy satisfecho con 5 segundos con 32 como PARALLELISM_NUMBER, pero estoy preocupado por las consecuencias.

  • ¿Está bien para mantener a 32?
  • ¿Cuál es el PARALLELISM_NUMBER ideal?
  • ¿Qué tengo que tener en cuenta a la hora de decidir su valor?

Estoy corriendo en un Mac 2,2 GHz I7

sysctl hw.physicalcpu hw.logicalcp
hw.physicalcpu: 4
hw.logicalcpu: 8

Esto es lo que processDTO hace:

private void processDTO(int dealerCode, int yearPeriod, int monthPeriod, AbstractDTO dto) throws FileAlreadyExistsInS3Exception {
    String flatJson = JsonFlattener.flatten(new JSONObject(dto).toString());
    String jsonFileName = dto.fileName() + JSON_TYPE;;
    String jsonFilePath = buildFilePathNew(dto.endpoint(), dealerCode, yearPeriod, monthPeriod, AWS_S3_JSON_ROOT_FOLDER);
    uploadFileToS3(jsonFilePath + jsonFileName, flatJson);
}
public void uploadFileToS3(String fileName, String fileContent) throws FileAlreadyExistsInS3Exception {
    if (s3client.doesObjectExist(bucketName, fileName)) {
        throw new FileAlreadyExistsInS3Exception(ErrorMessages.FILE_ALREADY_EXISTS_IN_S3.getMessage());
    }
    s3client.putObject(bucketName, fileName, fileContent);
}
fsakiyama:

He conseguido reducir a 8 segundos , gracias a todos sus votos consejos y explicaciones.

Dado que el cuello de botella era la carga de AWS S3, y se menciona una API no bloqueo de AWS, después de algunas investigaciones, descubrí que la clase TransferManager contiene un no-bloqueo de carga.

clase TransferManager

Así que en lugar de utilizar ForkJoinPool para aumentar el número de hilos, me quedé con el parallelStream simple:

        dtos.parallelStream().forEach(dto -> {
            try {
                processDTO(dealerCode, yearPeriod, monthPeriod, dto);
            } catch (FileAlreadyExistsInS3Exception e) {
                failedToUploadDTOs.add(e.getLocalizedMessage() + ": " + dto.fileName() + ".json");
            }
        });

Y el uploadToS3Method cambió un poco, en lugar de utilizar un AmazonS3 , he utilizado la TransferManager :

public Upload uploadAsyncFileToS3(String fileName, String fileContent) throws FileAlreadyExistsInS3Exception {
        if (s3client.doesObjectExist(bucketName, fileName)) {
            throw new FileAlreadyExistsInS3Exception(ErrorMessages.FILE_ALREADY_EXISTS_IN_S3.getMessage());
        }
        InputStream targetStream = new ByteArrayInputStream(fileContent.getBytes());
        ObjectMetadata metadata = new ObjectMetadata();
        metadata.setContentLength(fileContent.getBytes().length);
        return transferManager.upload(bucketName, fileName, targetStream, metadata);
}

De esta manera, cuando la carga se llama, no espera a que termine, dejando que otra DTO para ser procesados. Cuando se procesan todas DTO, puedo comprobar su estado de carga para ver posibles errores (fuera de la primera forEach)

Supongo que te gusta

Origin http://43.154.161.224:23101/article/api/json?id=136964&siteId=1
Recomendado
Clasificación