From ce15025c8d030b926a21824a77d77cf7a199d8d3 Mon Sep 17 00:00:00 2001 From: Klaus Date: Tue, 7 Nov 2023 16:47:25 +0900 Subject: [PATCH 1/2] =?UTF-8?q?=EC=BD=98=ED=85=90=EC=B8=A0=20=EB=A9=94?= =?UTF-8?q?=EC=9D=B8=20API=20-=20=EC=BD=94=EB=A3=A8=ED=8B=B4=20=EC=A0=81?= =?UTF-8?q?=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle.kts | 2 + .../sodalive/common/SemaphoreManager.kt | 10 ++ .../sodalive/configs/SemaphoreConfig.kt | 14 ++ .../main/AudioContentMainController.kt | 2 +- .../content/main/AudioContentMainService.kt | 136 ++++++++++++------ 5 files changed, 116 insertions(+), 48 deletions(-) create mode 100644 src/main/kotlin/kr/co/vividnext/sodalive/common/SemaphoreManager.kt create mode 100644 src/main/kotlin/kr/co/vividnext/sodalive/configs/SemaphoreConfig.kt diff --git a/build.gradle.kts b/build.gradle.kts index 84666cf..ff88d2f 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -55,6 +55,8 @@ dependencies { implementation("org.json:json:20230227") implementation("com.google.code.findbugs:jsr305:3.0.2") + implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3") + // firebase admin sdk implementation("com.google.firebase:firebase-admin:9.2.0") diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/common/SemaphoreManager.kt b/src/main/kotlin/kr/co/vividnext/sodalive/common/SemaphoreManager.kt new file mode 100644 index 0000000..b840ea1 --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/common/SemaphoreManager.kt @@ -0,0 +1,10 @@ +package kr.co.vividnext.sodalive.common + +import kotlinx.coroutines.sync.Semaphore +import kotlinx.coroutines.sync.withPermit + +class SemaphoreManager(private val semaphore: Semaphore) { + suspend fun withPermit(block: suspend () -> T): T { + return semaphore.withPermit { block() } + } +} diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/configs/SemaphoreConfig.kt b/src/main/kotlin/kr/co/vividnext/sodalive/configs/SemaphoreConfig.kt new file mode 100644 index 0000000..422a09c --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/configs/SemaphoreConfig.kt @@ -0,0 +1,14 @@ +package kr.co.vividnext.sodalive.configs + +import kotlinx.coroutines.sync.Semaphore +import kr.co.vividnext.sodalive.common.SemaphoreManager +import org.springframework.context.annotation.Bean +import org.springframework.context.annotation.Configuration + +@Configuration +class SemaphoreConfig { + @Bean + fun semaphoreManager(): SemaphoreManager { + return SemaphoreManager(Semaphore(4)) + } +} diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/content/main/AudioContentMainController.kt b/src/main/kotlin/kr/co/vividnext/sodalive/content/main/AudioContentMainController.kt index 18c4884..e78b513 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/content/main/AudioContentMainController.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/content/main/AudioContentMainController.kt @@ -15,7 +15,7 @@ import org.springframework.web.bind.annotation.RestController class AudioContentMainController(private val service: AudioContentMainService) { @GetMapping - fun getMain( + suspend fun getMain( @AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member? ) = run { if (member == null) throw SodaException("로그인 정보를 확인해주세요.") diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/content/main/AudioContentMainService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/content/main/AudioContentMainService.kt index 9808a59..8a4b387 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/content/main/AudioContentMainService.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/content/main/AudioContentMainService.kt @@ -1,5 +1,8 @@ package kr.co.vividnext.sodalive.content.main +import kotlinx.coroutines.async +import kotlinx.coroutines.coroutineScope +import kr.co.vividnext.sodalive.common.SemaphoreManager import kr.co.vividnext.sodalive.content.AudioContentRepository import kr.co.vividnext.sodalive.content.AudioContentService import kr.co.vividnext.sodalive.content.main.banner.AudioContentBannerType @@ -26,64 +29,100 @@ class AudioContentMainService( private val orderService: OrderService, private val audioContentThemeRepository: AudioContentThemeQueryRepository, + private val semaphoreManager: SemaphoreManager, + @Value("\${cloud.aws.cloud-front.host}") private val imageHost: String ) { - @Cacheable(cacheNames = ["default"], key = "'contentMain:' + #memberId + ':' + #isAdult") - fun getMain(memberId: Long, isAdult: Boolean): GetAudioContentMainResponse { - // 2주일 이내에 콘텐츠를 올린 크리에이터 20명 조회 - val newContentUploadCreatorList = getNewContentUploadCreatorList(memberId = memberId, isAdult = isAdult) + suspend fun getMain(memberId: Long, isAdult: Boolean): GetAudioContentMainResponse { + return coroutineScope { + // 2주일 이내에 콘텐츠를 올린 크리에이터 20명 조회 + val newContentUploadCreatorList = async { + semaphoreManager.withPermit { + getNewContentUploadCreatorList(memberId = memberId, isAdult = isAdult) + } + } - val bannerList = getAudioContentMainBannerList(memberId = memberId, isAdult = isAdult) + val bannerList = async { + semaphoreManager.withPermit { + getAudioContentMainBannerList(memberId = memberId, isAdult = isAdult) + } + } - // 구매목록 20개 - val orderList = orderService.getAudioContentMainOrderList( - memberId = memberId, - limit = 20 - ) + // 구매목록 20개 + val orderList = async { + semaphoreManager.withPermit { + orderService.getAudioContentMainOrderList( + memberId = memberId, + limit = 20 + ) + } + } - // 콘텐츠 테마 - val themeList = audioContentThemeRepository.getActiveThemeOfContent(isAdult = isAdult) + // 콘텐츠 테마 + val themeList = async { + semaphoreManager.withPermit { + audioContentThemeRepository.getActiveThemeOfContent(isAdult = isAdult) + } + } - // 새 콘텐츠 20개 - 시간 내림차순 정렬 - val newContentList = repository.findByTheme( - cloudfrontHost = imageHost, - isAdult = isAdult - ) - .asSequence() - .filter { !blockMemberRepository.isBlocked(blockedMemberId = memberId, memberId = it.creatorId) } - .toList() + // 새 콘텐츠 20개 - 시간 내림차순 정렬 + val newContentList = async { + semaphoreManager.withPermit { + repository.findByTheme( + cloudfrontHost = imageHost, + isAdult = isAdult + ) + .asSequence() + .filter { + !blockMemberRepository.isBlocked( + blockedMemberId = memberId, + memberId = it.creatorId + ) + } + .toList() + } + } - val curationList = getAudioContentCurationList(memberId = memberId, isAdult = isAdult) + val curationList = async { + semaphoreManager.withPermit { + getAudioContentCurationList(memberId = memberId, isAdult = isAdult) + } + } - val currentDateTime = LocalDateTime.now() - val startDate = currentDateTime - .minusWeeks(1) - .with(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY)) - .withHour(15) - .withMinute(0) - .withSecond(0) - val endDate = startDate.plusDays(7) + val currentDateTime = LocalDateTime.now() + val startDate = currentDateTime + .minusWeeks(1) + .with(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY)) + .withHour(15) + .withMinute(0) + .withSecond(0) + val endDate = startDate.plusDays(7) - val contentRankingSortTypeList = audioContentService.getContentRankingSortTypeList() - val contentRanking = audioContentService.getAudioContentRanking( - isAdult = isAdult, - startDate = startDate, - endDate = endDate, - offset = 0, - limit = 12 - ) + val contentRankingSortTypeList = audioContentService.getContentRankingSortTypeList() + val contentRanking = async { + semaphoreManager.withPermit { + audioContentService.getAudioContentRanking( + isAdult = isAdult, + startDate = startDate, + endDate = endDate, + offset = 0, + limit = 12 + ) + } + } - return GetAudioContentMainResponse( - newContentUploadCreatorList = newContentUploadCreatorList, - bannerList = bannerList, - orderList = orderList, - themeList = themeList, - newContentList = newContentList, - curationList = curationList, - contentRankingSortTypeList = contentRankingSortTypeList, - contentRanking = contentRanking - ) + GetAudioContentMainResponse( + newContentUploadCreatorList = newContentUploadCreatorList.await(), + bannerList = bannerList.await(), + orderList = orderList.await(), + themeList = themeList.await(), + newContentList = newContentList.await(), + curationList = curationList.await(), + contentRankingSortTypeList = contentRankingSortTypeList, + contentRanking = contentRanking.await() + ) + } } fun getThemeList(member: Member): List { @@ -116,6 +155,7 @@ class AudioContentMainService( return GetNewContentAllResponse(totalCount, items) } + @Cacheable(cacheNames = ["default"], key = "'newContentUploadCreatorList:' + #memberId + ':' + #isAdult") fun getNewContentUploadCreatorList(memberId: Long, isAdult: Boolean): List { return repository.getNewContentUploadCreatorList( cloudfrontHost = imageHost, @@ -126,6 +166,7 @@ class AudioContentMainService( .toList() } + @Cacheable(cacheNames = ["default"], key = "'contentMainBannerList:' + #memberId + ':' + #isAdult") fun getAudioContentMainBannerList(memberId: Long, isAdult: Boolean) = repository.getAudioContentMainBannerList(isAdult = isAdult) .asSequence() @@ -174,6 +215,7 @@ class AudioContentMainService( } .toList() + @Cacheable(cacheNames = ["default"], key = "'contentCurationList:' + #memberId + ':' + #isAdult") fun getAudioContentCurationList(memberId: Long, isAdult: Boolean) = repository.getAudioContentCurations(isAdult = isAdult) .asSequence() -- 2.40.1 From 9a394b7dae2b529614e1725fad63cc018c351754 Mon Sep 17 00:00:00 2001 From: Klaus Date: Tue, 7 Nov 2023 17:12:58 +0900 Subject: [PATCH 2/2] =?UTF-8?q?=EC=BD=98=ED=85=90=EC=B8=A0=20=EB=A9=94?= =?UTF-8?q?=EC=9D=B8=20API=20-=20=EC=BD=94=EB=A3=A8=ED=8B=B4=20=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle.kts | 2 - .../sodalive/common/SemaphoreManager.kt | 10 -- .../sodalive/configs/SemaphoreConfig.kt | 14 -- .../main/AudioContentMainController.kt | 2 +- .../content/main/AudioContentMainService.kt | 143 +++++++----------- 5 files changed, 55 insertions(+), 116 deletions(-) delete mode 100644 src/main/kotlin/kr/co/vividnext/sodalive/common/SemaphoreManager.kt delete mode 100644 src/main/kotlin/kr/co/vividnext/sodalive/configs/SemaphoreConfig.kt diff --git a/build.gradle.kts b/build.gradle.kts index ff88d2f..84666cf 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -55,8 +55,6 @@ dependencies { implementation("org.json:json:20230227") implementation("com.google.code.findbugs:jsr305:3.0.2") - implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3") - // firebase admin sdk implementation("com.google.firebase:firebase-admin:9.2.0") diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/common/SemaphoreManager.kt b/src/main/kotlin/kr/co/vividnext/sodalive/common/SemaphoreManager.kt deleted file mode 100644 index b840ea1..0000000 --- a/src/main/kotlin/kr/co/vividnext/sodalive/common/SemaphoreManager.kt +++ /dev/null @@ -1,10 +0,0 @@ -package kr.co.vividnext.sodalive.common - -import kotlinx.coroutines.sync.Semaphore -import kotlinx.coroutines.sync.withPermit - -class SemaphoreManager(private val semaphore: Semaphore) { - suspend fun withPermit(block: suspend () -> T): T { - return semaphore.withPermit { block() } - } -} diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/configs/SemaphoreConfig.kt b/src/main/kotlin/kr/co/vividnext/sodalive/configs/SemaphoreConfig.kt deleted file mode 100644 index 422a09c..0000000 --- a/src/main/kotlin/kr/co/vividnext/sodalive/configs/SemaphoreConfig.kt +++ /dev/null @@ -1,14 +0,0 @@ -package kr.co.vividnext.sodalive.configs - -import kotlinx.coroutines.sync.Semaphore -import kr.co.vividnext.sodalive.common.SemaphoreManager -import org.springframework.context.annotation.Bean -import org.springframework.context.annotation.Configuration - -@Configuration -class SemaphoreConfig { - @Bean - fun semaphoreManager(): SemaphoreManager { - return SemaphoreManager(Semaphore(4)) - } -} diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/content/main/AudioContentMainController.kt b/src/main/kotlin/kr/co/vividnext/sodalive/content/main/AudioContentMainController.kt index e78b513..18c4884 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/content/main/AudioContentMainController.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/content/main/AudioContentMainController.kt @@ -15,7 +15,7 @@ import org.springframework.web.bind.annotation.RestController class AudioContentMainController(private val service: AudioContentMainService) { @GetMapping - suspend fun getMain( + fun getMain( @AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member? ) = run { if (member == null) throw SodaException("로그인 정보를 확인해주세요.") diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/content/main/AudioContentMainService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/content/main/AudioContentMainService.kt index 8a4b387..5804afd 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/content/main/AudioContentMainService.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/content/main/AudioContentMainService.kt @@ -1,8 +1,5 @@ package kr.co.vividnext.sodalive.content.main -import kotlinx.coroutines.async -import kotlinx.coroutines.coroutineScope -import kr.co.vividnext.sodalive.common.SemaphoreManager import kr.co.vividnext.sodalive.content.AudioContentRepository import kr.co.vividnext.sodalive.content.AudioContentService import kr.co.vividnext.sodalive.content.main.banner.AudioContentBannerType @@ -29,100 +26,68 @@ class AudioContentMainService( private val orderService: OrderService, private val audioContentThemeRepository: AudioContentThemeQueryRepository, - private val semaphoreManager: SemaphoreManager, - @Value("\${cloud.aws.cloud-front.host}") private val imageHost: String ) { - suspend fun getMain(memberId: Long, isAdult: Boolean): GetAudioContentMainResponse { - return coroutineScope { - // 2주일 이내에 콘텐츠를 올린 크리에이터 20명 조회 - val newContentUploadCreatorList = async { - semaphoreManager.withPermit { - getNewContentUploadCreatorList(memberId = memberId, isAdult = isAdult) - } + fun getMain(memberId: Long, isAdult: Boolean): GetAudioContentMainResponse { + // 2주일 이내에 콘텐츠를 올린 크리에이터 20명 조회 + val newContentUploadCreatorList = getNewContentUploadCreatorList(memberId = memberId, isAdult = isAdult) + + val bannerList = getAudioContentMainBannerList(memberId = memberId, isAdult = isAdult) + + // 구매목록 20개 + val orderList = orderService.getAudioContentMainOrderList( + memberId = memberId, + limit = 20 + ) + + // 콘텐츠 테마 + val themeList = audioContentThemeRepository.getActiveThemeOfContent(isAdult = isAdult) + + // 새 콘텐츠 20개 - 시간 내림차순 정렬 + val newContentList = repository.findByTheme( + cloudfrontHost = imageHost, + isAdult = isAdult + ) + .asSequence() + .filter { + !blockMemberRepository.isBlocked( + blockedMemberId = memberId, + memberId = it.creatorId + ) } + .toList() - val bannerList = async { - semaphoreManager.withPermit { - getAudioContentMainBannerList(memberId = memberId, isAdult = isAdult) - } - } + val curationList = getAudioContentCurationList(memberId = memberId, isAdult = isAdult) - // 구매목록 20개 - val orderList = async { - semaphoreManager.withPermit { - orderService.getAudioContentMainOrderList( - memberId = memberId, - limit = 20 - ) - } - } + val currentDateTime = LocalDateTime.now() + val startDate = currentDateTime + .minusWeeks(1) + .with(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY)) + .withHour(15) + .withMinute(0) + .withSecond(0) + val endDate = startDate.plusDays(7) - // 콘텐츠 테마 - val themeList = async { - semaphoreManager.withPermit { - audioContentThemeRepository.getActiveThemeOfContent(isAdult = isAdult) - } - } + val contentRankingSortTypeList = audioContentService.getContentRankingSortTypeList() + val contentRanking = audioContentService.getAudioContentRanking( + isAdult = isAdult, + startDate = startDate, + endDate = endDate, + offset = 0, + limit = 12 + ) - // 새 콘텐츠 20개 - 시간 내림차순 정렬 - val newContentList = async { - semaphoreManager.withPermit { - repository.findByTheme( - cloudfrontHost = imageHost, - isAdult = isAdult - ) - .asSequence() - .filter { - !blockMemberRepository.isBlocked( - blockedMemberId = memberId, - memberId = it.creatorId - ) - } - .toList() - } - } - - val curationList = async { - semaphoreManager.withPermit { - getAudioContentCurationList(memberId = memberId, isAdult = isAdult) - } - } - - val currentDateTime = LocalDateTime.now() - val startDate = currentDateTime - .minusWeeks(1) - .with(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY)) - .withHour(15) - .withMinute(0) - .withSecond(0) - val endDate = startDate.plusDays(7) - - val contentRankingSortTypeList = audioContentService.getContentRankingSortTypeList() - val contentRanking = async { - semaphoreManager.withPermit { - audioContentService.getAudioContentRanking( - isAdult = isAdult, - startDate = startDate, - endDate = endDate, - offset = 0, - limit = 12 - ) - } - } - - GetAudioContentMainResponse( - newContentUploadCreatorList = newContentUploadCreatorList.await(), - bannerList = bannerList.await(), - orderList = orderList.await(), - themeList = themeList.await(), - newContentList = newContentList.await(), - curationList = curationList.await(), - contentRankingSortTypeList = contentRankingSortTypeList, - contentRanking = contentRanking.await() - ) - } + return GetAudioContentMainResponse( + newContentUploadCreatorList = newContentUploadCreatorList, + bannerList = bannerList, + orderList = orderList, + themeList = themeList, + newContentList = newContentList, + curationList = curationList, + contentRankingSortTypeList = contentRankingSortTypeList, + contentRanking = contentRanking + ) } fun getThemeList(member: Member): List { -- 2.40.1