From 1d565908a5c09986f4fcd2f696ab977d03153c55 Mon Sep 17 00:00:00 2001 From: JIUNG YANG Date: Sat, 20 Jul 2024 19:42:01 +0900 Subject: [PATCH 1/2] SB-191 (Feat) : Add AsyncConfig MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit SB-191 (Feat) : Add AsyncConfig 비동기 처리를 위한 AsyncConfig 추가 --- .../common/config/async/AsyncConfig.java | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 warehouse/src/main/java/warehouse/common/config/async/AsyncConfig.java diff --git a/warehouse/src/main/java/warehouse/common/config/async/AsyncConfig.java b/warehouse/src/main/java/warehouse/common/config/async/AsyncConfig.java new file mode 100644 index 00000000..d6e774c4 --- /dev/null +++ b/warehouse/src/main/java/warehouse/common/config/async/AsyncConfig.java @@ -0,0 +1,25 @@ +package warehouse.common.config.async; + +import java.util.concurrent.Executor; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.scheduling.annotation.EnableAsync; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; + +@EnableAsync +@Configuration +public class AsyncConfig { + + @Bean(name = "imageUploadExecutor") + public Executor imageUploadExecutor() { + + ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); + executor.setCorePoolSize(36); + executor.setMaxPoolSize(50); + executor.setQueueCapacity(100); + executor.initialize(); + + return executor; + } +} + From e630640c3bd705234d3dbfaa7c3373e0e1080e5b Mon Sep 17 00:00:00 2001 From: JIUNG YANG Date: Sat, 20 Jul 2024 19:47:49 +0900 Subject: [PATCH 2/2] SB-191 (Refactor) : Asynchronous Image Upload MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit SB-191 (Refactor) : Asynchronous Image Upload 이미지 업로드 속도 개선을 위한 이미지 저장 방식을 비동기로 변경 --- .../domain/image/business/ImageBusiness.java | 57 ++++++++++++++----- .../domain/image/service/ImageService.java | 2 + 2 files changed, 44 insertions(+), 15 deletions(-) diff --git a/warehouse/src/main/java/warehouse/domain/image/business/ImageBusiness.java b/warehouse/src/main/java/warehouse/domain/image/business/ImageBusiness.java index bc602030..ee5fcbfe 100644 --- a/warehouse/src/main/java/warehouse/domain/image/business/ImageBusiness.java +++ b/warehouse/src/main/java/warehouse/domain/image/business/ImageBusiness.java @@ -1,19 +1,22 @@ package warehouse.domain.image.business; import db.domain.image.ImageEntity; -import db.domain.image.enums.ImageKind; import global.annotation.Business; +import java.util.ArrayList; import java.util.List; -import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.CompletionException; +import java.util.concurrent.Executor; import java.util.stream.Collectors; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.web.context.request.RequestAttributes; +import org.springframework.web.context.request.RequestContextHolder; import warehouse.common.error.ImageErrorCode; import warehouse.common.exception.image.ImageStorageException; -import warehouse.domain.goods.controller.model.GoodsRequest; import warehouse.domain.goods.converter.GoodsConverter; import warehouse.domain.image.controller.model.ImageListRequest; -import warehouse.domain.image.controller.model.ImageListResponse; import warehouse.domain.image.controller.model.ImageRequest; import warehouse.domain.image.controller.model.ImageResponse; import warehouse.domain.image.converter.ImageConverter; @@ -28,24 +31,50 @@ public class ImageBusiness { private final ImageConverter imageConverter; private final GoodsConverter goodsConverter; - public ImageResponse uploadImage(ImageRequest request) { + @Qualifier("imageUploadExecutor") + private final Executor executor; + public ImageResponse uploadImage(ImageRequest request) { if (request.getFile().isEmpty()) { throw new ImageStorageException(ImageErrorCode.IMAGE_STORAGE_ERROR); } - - return imageUploadBizLogic(request); + return imageConverter.toResponse(imageUploadBizLogic(request)); } public List uploadImageList(ImageListRequest listRequest) { + RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes(); - return imageConverter.toRequestList(listRequest).stream() - .map(this::uploadImage).collect(Collectors.toList()); + List> futures = imageConverter.toRequestList(listRequest).stream() + .map(it -> CompletableFuture.supplyAsync(() -> { + try { + RequestContextHolder.setRequestAttributes(requestAttributes); + return imageUploadBizLogic(it); + } finally { + RequestContextHolder.resetRequestAttributes(); + } + }, executor)).toList(); + return getImageResponsesFromFutures(futures); + } + + private List getImageResponsesFromFutures(List> futures) { + List imageEntityList = new ArrayList<>(); + futures.forEach(it -> { + try { + imageEntityList.add(it.join()); + } catch (CompletionException e) { + deleteImage(imageEntityList); + throw new ImageStorageException(ImageErrorCode.IMAGE_STORAGE_ERROR); + } + }); + return imageConverter.toResponseList(imageEntityList); + } + + private void deleteImage(List imageEntityList) { + imageEntityList.forEach(imageService::deleteImageDB); } - - public List getImageUrlListBy(Long goodsId) { + public List getImageUrlListBy(Long goodsId) { return imageService.getImageUrlList(goodsId).stream() .map(imageConverter::toResponse).collect(Collectors.toList()); } @@ -54,11 +83,9 @@ public byte[] getImageFile(String filepath) { return imageService.getImageFileByteList(filepath); } - private ImageResponse imageUploadBizLogic(ImageRequest request) { + private ImageEntity imageUploadBizLogic(ImageRequest request) { ImageEntity entity = imageConverter.toEntity(request); imageService.uploadImage(request.getFile(), entity); - ImageEntity newEntity = imageService.saveImageDataToDB(entity); - return imageConverter.toResponse(newEntity); + return imageService.saveImageDataToDB(entity); } - } diff --git a/warehouse/src/main/java/warehouse/domain/image/service/ImageService.java b/warehouse/src/main/java/warehouse/domain/image/service/ImageService.java index 9ab82592..a184d11c 100644 --- a/warehouse/src/main/java/warehouse/domain/image/service/ImageService.java +++ b/warehouse/src/main/java/warehouse/domain/image/service/ImageService.java @@ -82,6 +82,8 @@ public void updateImageDB(ImageEntity th) { saveImageDataToDB(th); } + public void deleteImageDB(ImageEntity th) { imageRepository.deleteById(th.getId()); } + public List getImagesByImageIdList(List ids) { return ids.stream().map(this::getImageByImageId).collect(Collectors.toList()); }