Как можно улучшить производительность, а также избежать ошибки кучи в моем приложении при загрузке большого файла CSV? - PullRequest
0 голосов
/ 20 сентября 2019

Привет, у меня есть Java-бэкэнд с пружинной загрузкой и угловой интерфейс.У меня есть сценарий, в котором пользователь может нажать на кнопку загрузить CSV в моем угловом интерфейсе, и он сделает вызов API Rest.Остальные вызовы API обрабатываются ресурсным методом, он запрашивает эластичный поиск, конвертирует документы в список объектов, а затем мы возвращаем файл пользователю.Он работает нормально, однако, если я скачаю огромный набор результатов, он выдаст кучу, превышающую исключение, и к тому же он очень медленный.Я попробовал пару стратегий, чтобы оптимизировать это, но я не преуспел в этом.поэтому, пожалуйста, помогите, если вы найдете, какие изменения / оптимизацию я могу внести в мой код, чтобы улучшить производительность и проблемы с памятью при загрузке файла.Я отправляю код, как показано ниже.

MessageHistoryReportingResource.java

@RestController
@RequestMapping("/api")
public class MessageHistoryReportingResource {

@GetMapping("/message-history/csv")
    @Timed
    @Secured({ AuthoritiesConstants.CAMPAIGN_MANAGER, AuthoritiesConstants.TEAM_MEMBER,AuthoritiesConstants.ACCOUNT_ADMIN,
             AuthoritiesConstants.GLOBAL_ADMIN })
    public ResponseEntity<MessageHistoryCSVWrapper>  getMessageHistoriesCSV(MessageHistoryFilterRequest messageHistoryFilterRequest)
            throws IOException {

        log.debug("REST request to download message histories: {}", messageHistoryFilterRequest);

        byte[] bytes =
                CsvUtil.createByteArray(this.messageHistoryReportingService.findCSV(messageHistoryFilterRequest));

        return ResponseEntity.ok().body(new MessageHistoryCSVWrapper(new String(bytes)));
    }


}

MessageHistoryReportingServiceImpl

@Service
public class MessageHistoryReportingServiceImpl implements MessageHistoryReportingService {
@Override
    public List<String> findCSV(MessageHistoryFilterRequest messageHistoryFilterRequest) {

        List<MessageHistory> messageHistories = messageHistoryReportingRepository
                .findAllCSV(messageHistoryFilterRequest);

        List<String> csvLines = new ArrayList<>();
        String header = String.join(",", csvHeader);
        csvLines.add(header);

        List<String> msgHistoriesAsLines = messageHistories.stream().map(this::convertMessageHistoryAsCSV)
                .collect(Collectors.toList());
        csvLines.addAll(msgHistoriesAsLines);

        return csvLines;
    }

    private String convertMessageHistoryAsCSV(MessageHistory msgHistory) {

        return String.join(",", msgHistory.getTimeStamp().toString(), msgHistory.getRecipient(), msgHistory.getSender(),
                msgHistory.getCampaignName(), msgHistory.getFlightName(), msgHistory.getStatus(), msgHistory.getError(),
                msgHistory.getMessageText());

    }

}

MessageHistoryReportingRepository

@Repository
public class MessageHistoryReportingRepository {

    private RestHighLevelClient elasticSearchClient;

    public List<MessageHistory> findAllCSV(MessageHistoryFilterRequest messageHistoryFilterRequest) {

        SearchRequestBuilder searchRequestBuilder = new SearchRequestBuilder(ELASTIC_SEARCH_INDEX);

        SearchRequest searchRequest = searchRequestBuilder
                .timeout(300, TimeUnit.SECONDS)
                .range(messageHistoryFilterRequest.getStartDate(), messageHistoryFilterRequest.getEndDate())
                .msisdn(messageHistoryFilterRequest.getMsisdn()).accountId(messageHistoryFilterRequest.getAccountId())
                .campaignId(messageHistoryFilterRequest.getCampaignId())
                .flightId(messageHistoryFilterRequest.getFlightId())
                .cdrStatus(messageHistoryFilterRequest.getCdrStatus())
                .inventoryCode(messageHistoryFilterRequest.getInventoryCode()).sort()
                .fetchSourceContext(true, INCLUDES, EXCLUDES).scroll(TimeValue.timeValueMinutes(1L)).build();

        SearchResponse searchResponse = null;

        List<MessageHistory> messageHistories = null;

        try {

            searchResponse = elasticSearchClient.search(searchRequest, RequestOptions.DEFAULT);

            String scrollId = searchResponse.getScrollId();

            SearchHit[] searchHits = searchResponse.getHits().getHits();

            Collection<SearchHit> searchHitCollection = new LinkedList<SearchHit>();

            while (searchHits != null && searchHits.length > 0) {

                searchHitCollection.addAll(Arrays.asList(searchHits));

                SearchScrollRequest scrollRequest = new SearchScrollRequest(scrollId);

                scrollRequest.scroll(new Scroll(TimeValue.timeValueMinutes(1l)));

                searchResponse = elasticSearchClient.scroll(scrollRequest, RequestOptions.DEFAULT);
                scrollId = searchResponse.getScrollId();
                searchHits = searchResponse.getHits().getHits();
            }

            ClearScrollRequest clearScrollRequest = new ClearScrollRequest();
            clearScrollRequest.addScrollId(scrollId);
            ClearScrollResponse clearScrollResponse = elasticSearchClient.clearScroll(clearScrollRequest,
                    RequestOptions.DEFAULT);
            boolean succeeded = clearScrollResponse.isSucceeded();
            messageHistories = searchHitCollection.stream().map(this::translate).collect(Collectors.toList());

        } catch (IOException e) {
            e.printStackTrace();
            throw new RuntimeException(e);
        }

        return messageHistories;
    }

    private MessageHistory translate(SearchHit searchHit) {
        try {
            return objectMapper.readValue(searchHit.getSourceAsString(), MessageHistory.class);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

}

так что просто, чтобы объяснить немного подробнее.в моем MessageHistoryReportingRepository метод findAllCSV запрашивает эластичный поиск, использует прокрутку, продолжает извлекать записи и помещает его в список Collection searchHitCollection.затем я перевожу коллекцию, используя translate (SearchHit searchHit), который используется в потоке api searchHitCollection.stream (). map (this :: translate) .collect (Collectors.toList ());.

, затемСписок, возвращаемый из findAllCSV, возвращается в MessageHistoryReportingServiceImpl # findCSV, где он перебирает список объектов и преобразует каждый объект в строку.затем в моем MessageHistoryResource я буду вызывать CSVUtil.createByArray, чтобы преобразовать его в byte [] для возврата в пользовательский интерфейс.

CsvUtil

открытый класс CsvUtil {

private static final Logger log = LoggerFactory.getLogger(CsvUtil.class);

/**
 * The List of recipients is converted to a byte array formatted for a csv.
 * A recipient per line
 *
 * @param csvRows list of contact list recipients
 * @return The byte array of the recipients
 */
public static byte[] createByteArray(List<String> csvRows) {
    byte[] listToBytes = new byte[0];
    try (ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
         BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(outputStream))) {
        csvRows.forEach(row -> {
            try {
                bw.write(row);
                bw.write("\n");
            } catch (IOException e) {
                log.error("Error writing recipients to outputstream ", e);
            }
        });
        bw.close();
        listToBytes = outputStream.toByteArray();
    } catch (IOException e) {
        log.error("Error writing recipients to outputstream ", e);
    }

    return listToBytes;
}

}

MessageHistory.java

public class MessageHistory {

    @JsonProperty("inventory")
    private String inventory;

    @JsonProperty("msg_text")
    private String messageText;

    @JsonProperty("status")
    private String status;

    @JsonProperty("@timestamp")
    private Instant timeStamp;

    @JsonProperty("o_error")
    private String error;

    @JsonProperty("flight_id")
    private UUID flightId;

    @JsonProperty("recipient")
    private String recipient;

    @JsonProperty("account_id")
    private UUID accountId;

    @JsonProperty("sender")
    private String sender;

    @JsonProperty("campaign_id")
    private UUID campaignId;

    @JsonProperty("nof_segments")
    private Integer segmentCount;

    @JsonProperty("@version")
    private Integer version;

    @JsonProperty("submission_ts")
    @JsonFormat(shape = JsonFormat.Shape.NUMBER_INT)
    private Instant submissionTimeStamp;

    @JsonProperty("delivery_ts")
    @JsonFormat(shape = JsonFormat.Shape.NUMBER_INT)
    private Instant deliveryTimeStamp;

    @JsonProperty("campaign_name")
    private String campaignName;

    @JsonProperty("flight_name")
    private String flightName;


    public String getInventory() {
        return inventory;
    }

    public void setInventory(String inventory) {
        this.inventory = inventory;
    }

    public String getMessageText() {
        return messageText;
    }

    public void setMessageText(String messageText) {
        this.messageText = messageText;
    }

    public String getStatus() {
        return status;
    }

    public void setStatus(String status) {
        this.status = status;
    }

    public Instant getTimeStamp() {
        return timeStamp;
    }

    public void setTimeStamp(Instant timeStamp) {
        this.timeStamp = timeStamp;
    }

    public String getError() {
        return error;
    }

    public void setError(String error) {
        this.error = error;
    }

    public UUID getFlightId() {
        return flightId;
    }

    public void setFlightId(UUID flightId) {
        this.flightId = flightId;
    }

    public String getRecipient() {
        return recipient;
    }

    public void setRecipient(String recipient) {
        this.recipient = recipient;
    }

    public String getSender() {
        return sender;
    }

    public void setSender(String sender) {
        this.sender = sender;
    }

    public UUID getAccountId() {
        return accountId;
    }

    public void setAccountId(UUID accountId) {
        this.accountId = accountId;
    }

    public UUID getCampaignId() {
        return campaignId;
    }

    public void setCampaignId(UUID campaignId) {
        this.campaignId = campaignId;
    }

    public Integer getSegmentCount() {
        return segmentCount;
    }

    public void setSegmentCount(Integer segmentCount) {
        this.segmentCount = segmentCount;
    }

    public Integer getVersion() {
        return version;
    }

    public void setVersion(Integer version) {
        this.version = version;
    }

    public Instant getSubmissionTimeStamp() {
        return submissionTimeStamp;
    }

    public void setSubmissionTimeStamp(Instant submissionTimeStamp) {
        this.submissionTimeStamp = submissionTimeStamp;
    }

    public Instant getDeliveryTimeStamp() {
        return deliveryTimeStamp;
    }

    public void setDeliveryTimeStamp(Instant deliveryTimeStamp) {
        this.deliveryTimeStamp = deliveryTimeStamp;
    }

    public String getCampaignName() {
        return campaignName;
    }

    public void setCampaignName(String campaignName) {
        this.campaignName = campaignName;
    }

    public String getFlightName() {
        return flightName;
    }

    public void setFlightName(String flightName) {
        this.flightName = flightName;
    }
}

очень ценю вашу помощь, если у вас есть некоторые предложения в коде для повышения производительности, улучшения оптимизации и проблемы с памятью.

спасибо

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...