From 96dd033be97dbaca0d43b03f24b0e7b7d35d25d6 Mon Sep 17 00:00:00 2001 From: yb__char Date: Fri, 13 Dec 2024 15:22:53 +0900 Subject: [PATCH 1/2] refactor: ThreadConfig --- .../global/config/thread/ThreadConfig.java | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 src/main/java/com/depromeet/global/config/thread/ThreadConfig.java diff --git a/src/main/java/com/depromeet/global/config/thread/ThreadConfig.java b/src/main/java/com/depromeet/global/config/thread/ThreadConfig.java new file mode 100644 index 00000000..54c0c5c1 --- /dev/null +++ b/src/main/java/com/depromeet/global/config/thread/ThreadConfig.java @@ -0,0 +1,24 @@ +package com.depromeet.global.config.thread; + +import java.util.concurrent.Executors; +import org.springframework.boot.autoconfigure.task.TaskExecutionAutoConfiguration; +import org.springframework.boot.web.embedded.tomcat.TomcatProtocolHandlerCustomizer; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.task.AsyncTaskExecutor; +import org.springframework.core.task.support.TaskExecutorAdapter; + +@Configuration +public class ThreadConfig { + + @Bean(TaskExecutionAutoConfiguration.APPLICATION_TASK_EXECUTOR_BEAN_NAME) + public AsyncTaskExecutor asyncTaskExecutor() { + return new TaskExecutorAdapter(Executors.newVirtualThreadPerTaskExecutor()); + } + + @Bean + public TomcatProtocolHandlerCustomizer protocolHandlerVirtualThreadExecutorCustomizer() { + return protocolHandler -> + protocolHandler.setExecutor(Executors.newVirtualThreadPerTaskExecutor()); + } +} From 34e4d3c9afb2550793f521c7a3f356a5f01e8146 Mon Sep 17 00:00:00 2001 From: yb__char Date: Sun, 15 Dec 2024 21:03:46 +0900 Subject: [PATCH 2/2] refactor: Virtual Thread Example --- .../application/MissionRecordService.java | 14 ++++++++- .../com/depromeet/global/util/MemberUtil.java | 29 +++++++++++++++---- 2 files changed, 36 insertions(+), 7 deletions(-) diff --git a/src/main/java/com/depromeet/domain/missionRecord/application/MissionRecordService.java b/src/main/java/com/depromeet/domain/missionRecord/application/MissionRecordService.java index aef58083..95241dbe 100644 --- a/src/main/java/com/depromeet/domain/missionRecord/application/MissionRecordService.java +++ b/src/main/java/com/depromeet/domain/missionRecord/application/MissionRecordService.java @@ -21,6 +21,9 @@ import java.time.YearMonth; import java.util.List; import java.util.Optional; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -38,11 +41,14 @@ public class MissionRecordService { private final MissionRecordRepository missionRecordRepository; private final MissionRecordTtlRepository missionRecordTtlRepository; + private static final ExecutorService VIRTUAL_THREAD_EXECUTOR = + Executors.newVirtualThreadPerTaskExecutor(); + public MissionRecordCreateResponse createMissionRecord(MissionRecordCreateRequest request) { long diffHour = Duration.between(request.startedAt(), request.finishedAt()).toHours(); validateMissionRecordDurationOverTime(diffHour); - final Mission mission = findMissionById(request.missionId()); + final Mission mission = findMissionByIdAsync(request.missionId()); final Member member = memberUtil.getCurrentMember(); Duration duration = @@ -81,6 +87,12 @@ private void validateMissionRecordExistsToday(Long missionId) { } } + private Mission findMissionByIdAsync(Long missionId) { + return CompletableFuture.supplyAsync( + () -> findMissionById(missionId), VIRTUAL_THREAD_EXECUTOR) + .join(); + } + private Mission findMissionById(Long missionId) { return missionRepository .findById(missionId) diff --git a/src/main/java/com/depromeet/global/util/MemberUtil.java b/src/main/java/com/depromeet/global/util/MemberUtil.java index 2e8a0d52..dc5b34c7 100644 --- a/src/main/java/com/depromeet/global/util/MemberUtil.java +++ b/src/main/java/com/depromeet/global/util/MemberUtil.java @@ -4,6 +4,9 @@ import com.depromeet.domain.member.domain.Member; import com.depromeet.global.error.exception.CustomException; import com.depromeet.global.error.exception.ErrorCode; +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Component; @@ -13,16 +16,30 @@ public class MemberUtil { private final SecurityUtil securityUtil; private final MemberRepository memberRepository; + private static final ExecutorService VIRTUAL_THREAD_EXECUTOR = + Executors.newVirtualThreadPerTaskExecutor(); public Member getCurrentMember() { - return memberRepository - .findById(securityUtil.getCurrentMemberId()) - .orElseThrow(() -> new CustomException(ErrorCode.MEMBER_NOT_FOUND)); + return CompletableFuture.supplyAsync( + () -> + memberRepository + .findById(securityUtil.getCurrentMemberId()) + .orElseThrow( + () -> + new CustomException( + ErrorCode.MEMBER_NOT_FOUND))) + .join(); } public Member getMemberByMemberId(Long memberId) { - return memberRepository - .findById(memberId) - .orElseThrow(() -> new CustomException(ErrorCode.MEMBER_NOT_FOUND)); + return CompletableFuture.supplyAsync( + () -> + memberRepository + .findById(memberId) + .orElseThrow( + () -> + new CustomException( + ErrorCode.MEMBER_NOT_FOUND))) + .join(); } }