스프링 스케줄러를 이용하여 콘텐츠 예약 오픈 설정
This commit is contained in:
parent
4097e5a133
commit
05592f94b9
|
@ -26,6 +26,7 @@ repositories {
|
|||
}
|
||||
|
||||
dependencies {
|
||||
implementation("org.springframework.boot:spring-boot-starter-aop")
|
||||
implementation("org.springframework.boot:spring-boot-starter-data-jpa")
|
||||
implementation("org.springframework.boot:spring-boot-starter-data-redis")
|
||||
implementation("org.springframework.boot:spring-boot-starter-security")
|
||||
|
@ -33,6 +34,7 @@ dependencies {
|
|||
implementation("com.fasterxml.jackson.module:jackson-module-kotlin")
|
||||
implementation("org.springframework.retry:spring-retry")
|
||||
implementation("org.jetbrains.kotlin:kotlin-reflect")
|
||||
implementation("org.redisson:redisson-spring-boot-starter:3.17.7")
|
||||
|
||||
// jwt
|
||||
implementation("io.jsonwebtoken:jjwt-api:0.11.5")
|
||||
|
|
|
@ -4,8 +4,10 @@ import org.springframework.boot.autoconfigure.SpringBootApplication
|
|||
import org.springframework.boot.runApplication
|
||||
import org.springframework.retry.annotation.EnableRetry
|
||||
import org.springframework.scheduling.annotation.EnableAsync
|
||||
import org.springframework.scheduling.annotation.EnableScheduling
|
||||
|
||||
@SpringBootApplication
|
||||
@EnableScheduling
|
||||
@EnableAsync
|
||||
@EnableRetry
|
||||
class SodaLiveApplication
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
package kr.co.vividnext.sodalive.common.annotation
|
||||
|
||||
import org.aspectj.lang.annotation.Aspect
|
||||
import org.aspectj.lang.annotation.Before
|
||||
import org.springframework.stereotype.Component
|
||||
|
||||
@Target(AnnotationTarget.FUNCTION)
|
||||
@Retention(AnnotationRetention.RUNTIME)
|
||||
annotation class SchedulerOnly
|
||||
|
||||
@Aspect
|
||||
@Component
|
||||
class SchedulerOnlyAspect {
|
||||
|
||||
@Before("@annotation(SchedulerOnly)")
|
||||
fun checkSchedulerAccess() {
|
||||
if (!isSchedulerThread()) {
|
||||
throw IllegalStateException("잘못된 접근입니다.")
|
||||
}
|
||||
}
|
||||
|
||||
private fun isSchedulerThread(): Boolean {
|
||||
// 스케줄러 스레드 여부를 판단하는 간단한 로직
|
||||
return Thread.currentThread().name.contains("scheduler")
|
||||
}
|
||||
}
|
|
@ -1,5 +1,8 @@
|
|||
package kr.co.vividnext.sodalive.configs
|
||||
|
||||
import org.redisson.Redisson
|
||||
import org.redisson.api.RedissonClient
|
||||
import org.redisson.config.Config
|
||||
import org.springframework.beans.factory.annotation.Value
|
||||
import org.springframework.cache.annotation.EnableCaching
|
||||
import org.springframework.context.annotation.Bean
|
||||
|
@ -26,6 +29,17 @@ class RedisConfig(
|
|||
@Value("\${spring.redis.port}")
|
||||
private val port: Int
|
||||
) {
|
||||
@Bean
|
||||
fun redissonClient(): RedissonClient {
|
||||
val config = Config()
|
||||
config.useSingleServer()
|
||||
.setAddress("redis://$host:$port")
|
||||
.setConnectionMinimumIdleSize(1) // 최소 유휴 연결: 1
|
||||
.setConnectionPoolSize(5) // 최대 연결 풀 크기: 5
|
||||
|
||||
return Redisson.create(config)
|
||||
}
|
||||
|
||||
@Bean
|
||||
fun redisConnectionFactory(): RedisConnectionFactory {
|
||||
val clientConfiguration = LettuceClientConfiguration.builder()
|
||||
|
|
|
@ -5,6 +5,7 @@ import com.fasterxml.jackson.databind.ObjectMapper
|
|||
import kr.co.vividnext.sodalive.aws.cloudfront.AudioContentCloudFront
|
||||
import kr.co.vividnext.sodalive.aws.s3.S3Uploader
|
||||
import kr.co.vividnext.sodalive.common.SodaException
|
||||
import kr.co.vividnext.sodalive.common.annotation.SchedulerOnly
|
||||
import kr.co.vividnext.sodalive.content.comment.AudioContentCommentRepository
|
||||
import kr.co.vividnext.sodalive.content.hashtag.AudioContentHashTag
|
||||
import kr.co.vividnext.sodalive.content.hashtag.HashTag
|
||||
|
@ -402,6 +403,7 @@ class AudioContentService(
|
|||
}
|
||||
}
|
||||
|
||||
@SchedulerOnly
|
||||
@Transactional
|
||||
fun releaseContent() {
|
||||
val contentIdList = repository.getNotReleaseContentId()
|
||||
|
|
|
@ -0,0 +1,32 @@
|
|||
package kr.co.vividnext.sodalive.scheduler
|
||||
|
||||
import kr.co.vividnext.sodalive.content.AudioContentService
|
||||
import org.redisson.api.RedissonClient
|
||||
import org.springframework.scheduling.annotation.Scheduled
|
||||
import org.springframework.stereotype.Service
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
@Service
|
||||
class AudioContentReleaseSchedulerService(
|
||||
private val redissonClient: RedissonClient,
|
||||
private val audioContentService: AudioContentService
|
||||
) {
|
||||
@Scheduled(fixedRate = 1000 * 60 * 5)
|
||||
fun release() {
|
||||
val lock = redissonClient.getLock("lock:audioContentRelease")
|
||||
|
||||
if (lock.tryLock(10, TimeUnit.SECONDS)) {
|
||||
try {
|
||||
println("락을 획득하여 배포를 시작합니다.")
|
||||
audioContentService.releaseContent()
|
||||
} finally {
|
||||
if (lock.isHeldByCurrentThread) {
|
||||
lock.unlock()
|
||||
println("락 해제")
|
||||
}
|
||||
}
|
||||
} else {
|
||||
println("락을 획득하지 못해서 배포를 건너뜁니다")
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue