diff --git a/.DS_Store b/.DS_Store index 038c7231..1c360592 100644 Binary files a/.DS_Store and b/.DS_Store differ diff --git a/build.gradle b/build.gradle index 7b7f9378..db1d3d69 100644 --- a/build.gradle +++ b/build.gradle @@ -77,6 +77,7 @@ dependencies { implementation 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310' implementation group: 'io.jsonwebtoken', name: 'jjwt-api', version: '0.11.5' + implementation 'org.redisson:redisson-spring-boot-starter:3.21.1' runtimeOnly group: 'io.jsonwebtoken', name: 'jjwt-impl', version: '0.11.5' runtimeOnly group: 'io.jsonwebtoken', name: 'jjwt-jackson', version: '0.11.5' if ( System.getProperty("os.name") == "Mac OS X" && System.getProperty("os.arch") == "aarch64") { diff --git a/src/main/java/swm_nm/morandi/aop/lock/MemberLockAspect.java b/src/main/java/swm_nm/morandi/aop/lock/MemberLockAspect.java index 0e2e4d73..df726751 100644 --- a/src/main/java/swm_nm/morandi/aop/lock/MemberLockAspect.java +++ b/src/main/java/swm_nm/morandi/aop/lock/MemberLockAspect.java @@ -1,12 +1,13 @@ package swm_nm.morandi.aop.lock; - import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; +import org.redisson.api.RLock; +import org.redisson.api.RedissonClient; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.stereotype.Component; import swm_nm.morandi.global.exception.MorandiException; @@ -21,37 +22,32 @@ @RequiredArgsConstructor public class MemberLockAspect { + private final RedissonClient redissonClient; + private final StringRedisTemplate redisTemplate; private final String MEMBER_LOCK_KEY = "memberLock"; - private final Integer MEMBER_LOCK_TTL = 5; - @Pointcut("@annotation(swm_nm.morandi.aop.annotation.MemberLock)") public void memberLockPointcut() { } @Around("memberLockPointcut()") - public Object MEMBERLock(ProceedingJoinPoint joinPoint) throws Throwable { + public Object MEMBERLock(ProceedingJoinPoint joinPoint) throws Throwable { Long memberId = SecurityUtils.getCurrentMemberId(); - String memberLockKey = String.format("%s:%d", MEMBER_LOCK_KEY,memberId); + String memberLockKey = String.format("%s:%d", MEMBER_LOCK_KEY, memberId); + RLock lock = redissonClient.getLock(memberLockKey); boolean locked = false; try { - if (tryLock(memberLockKey)) { - locked = true; - return joinPoint.proceed(); - } else { + locked = lock.tryLock(2, 5, TimeUnit.SECONDS); + if (!locked) { throw new MorandiException(LockErrorCode.MEMBER_LOCKED); } + return joinPoint.proceed(); } finally { - if(locked) { + if (locked) { unlock(memberLockKey); } } } - - private Boolean tryLock(String key) { - return redisTemplate.opsForValue().setIfAbsent(key, "locked", MEMBER_LOCK_TTL, TimeUnit.SECONDS); - } - private void unlock(String key) { redisTemplate.delete(key); } diff --git a/src/main/java/swm_nm/morandi/config/async/AsyncConfig.java b/src/main/java/swm_nm/morandi/config/async/AsyncConfig.java new file mode 100644 index 00000000..55278414 --- /dev/null +++ b/src/main/java/swm_nm/morandi/config/async/AsyncConfig.java @@ -0,0 +1,23 @@ +package swm_nm.morandi.config.async; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.task.TaskExecutor; +import org.springframework.scheduling.annotation.EnableAsync; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; + +@Configuration +@EnableAsync +public class AsyncConfig { + + @Bean(name = "taskExecutor") + public TaskExecutor taskExecutor() { + ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); + executor.setCorePoolSize(5); // 기본 스레드 풀 크기 + executor.setMaxPoolSize(10); // 최대 스레드 풀 크기 + executor.setQueueCapacity(25); // 큐의 용량 + executor.setThreadNamePrefix("Async-"); + executor.initialize(); + return executor; + } +} diff --git a/src/main/java/swm_nm/morandi/domain/codeSubmit/controller/TestController.java b/src/main/java/swm_nm/morandi/domain/codeSubmit/controller/TestController.java new file mode 100644 index 00000000..4df986c2 --- /dev/null +++ b/src/main/java/swm_nm/morandi/domain/codeSubmit/controller/TestController.java @@ -0,0 +1,19 @@ +package swm_nm.morandi.domain.codeSubmit.controller; + +import lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RestController; +import swm_nm.morandi.domain.codeSubmit.service.TestService; + +@RestController +@RequiredArgsConstructor +public class TestController { + + private final TestService testService; + + @PostMapping("/hello-world/{value}") + public String getData(@PathVariable("value") Integer value) throws InterruptedException { + return testService.test(value); + } +} diff --git a/src/main/java/swm_nm/morandi/domain/codeSubmit/service/TestService.java b/src/main/java/swm_nm/morandi/domain/codeSubmit/service/TestService.java new file mode 100644 index 00000000..20a4ef2a --- /dev/null +++ b/src/main/java/swm_nm/morandi/domain/codeSubmit/service/TestService.java @@ -0,0 +1,13 @@ +package swm_nm.morandi.domain.codeSubmit.service; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +@Service +@Slf4j +public class TestService { + public String test(int value) throws InterruptedException { + System.out.println("Response Message SSEID : " + 1 + " Count : " + value); + return "hello"; + } +}