Como melhorar o desempenho iteração mais de 130 itens de enviá-las para a AWS s3

fsakiyama:

Não tenho a iterar 130 de transferência de dados Objectos, e cada vez irá gerar um json para ser carregado para o AWS S3.

Sem melhorias, demora cerca de 90 segundos a completar todo o processo. Eu tentei usar lamba e não usando lamba, os mesmos 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");
    }
});

Depois de algumas investigações, cheguei à conclusão de que o método processDTO leva cerca 0.650ms por item para ser executado.

Minha primeira tentativa foi usar fluxos paralelos , e os resultados foram muito bons, levando cerca de 15 segundos para completar todo o processo:

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

Mas eu ainda preciso diminuir esse tempo. Eu pesquisei sobre como melhorar fluxos paralelos, e descobriu o ForkJoinPool truque:

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();

Infelizmente, os resultados foram um pouco confuso para mim.

  • Quando PARALLELISM_NUMBER é 8, que leva cerca de 13 segundo para completar todo o processo. Não é um grande melhorar.
  • Quando PARALLELISM_NUMBER é de 16, ele leva cerca de 8 segundos para completar todo o processo.
  • Quando PARALLELISM_NUMBER é 32, que leva cerca de 5 segundos para completar todo o processo.

Todos os testes foram feitos usando solicitações carteiro, chamando o método controlador que vai acabar-up iteração os 130 itens

Estou satisfeito com 5 segundos, usando 32 como PARALLELISM_NUMBER, mas estou preocupado com as consequências.

  • É ok para manter 32?
  • Qual é o PARALLELISM_NUMBER ideal?
  • O que eu tenho que manter em mente quando decidir o seu valor?

Estou rodando em um Mac 2.2GHz I7

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

Aqui está o que processDTO faz:

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:

I conseguiu reduzir para 8 segundos , graças a todos os seus conselhos úteis e explicações.

Desde o gargalo era o upload para AWS S3, e você mencionou uma API sem bloqueio na AWS, depois de algumas pesquisas, descobri que a classe TransferManager contém um non-blocking upload.

classe TransferManager

Então, em vez de usar ForkJoinPool para aumentar o número de threads, eu mantive a parallelStream simples:

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

E o uploadToS3Method mudou um pouco, em vez de usar um AmazonS3 , eu usei o 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);
}

Desta forma, quando o upload é chamado, ele não espera que ela termine, deixando outra DTO para ser processado. Quando tudo DTO são processados, eu verificar o seu estado de upload para ver possíveis erros (fora do primeiro forEach)

Acho que você gosta

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