Skip to content

Commit c62f94f

Browse files
authored
Merge pull request #612 from SWM-Morandi/dev
Redis 분산락에 Redisson 라이브러리를 활용
2 parents db80e18 + dae1a98 commit c62f94f

File tree

7 files changed

+73
-26
lines changed

7 files changed

+73
-26
lines changed

.DS_Store

0 Bytes
Binary file not shown.

build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ dependencies {
7777

7878
implementation 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310'
7979
implementation group: 'io.jsonwebtoken', name: 'jjwt-api', version: '0.11.5'
80+
implementation 'org.redisson:redisson-spring-boot-starter:3.21.1'
8081
runtimeOnly group: 'io.jsonwebtoken', name: 'jjwt-impl', version: '0.11.5'
8182
runtimeOnly group: 'io.jsonwebtoken', name: 'jjwt-jackson', version: '0.11.5'
8283
if ( System.getProperty("os.name") == "Mac OS X" && System.getProperty("os.arch") == "aarch64") {
Lines changed: 15 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,60 +1,52 @@
11
package swm_nm.morandi.aop.lock;
22

3-
43
import lombok.RequiredArgsConstructor;
54
import lombok.extern.slf4j.Slf4j;
65
import org.aspectj.lang.ProceedingJoinPoint;
76
import org.aspectj.lang.annotation.Around;
87
import org.aspectj.lang.annotation.Aspect;
98
import org.aspectj.lang.annotation.Pointcut;
9+
import org.redisson.api.RLock;
10+
import org.redisson.api.RedissonClient;
1011
import org.springframework.data.redis.core.StringRedisTemplate;
1112
import org.springframework.stereotype.Component;
1213
import swm_nm.morandi.global.exception.MorandiException;
1314
import swm_nm.morandi.global.exception.errorcode.LockErrorCode;
1415
import swm_nm.morandi.global.utils.SecurityUtils;
1516

1617
import java.util.concurrent.TimeUnit;
18+
import java.util.concurrent.locks.Lock;
1719

1820
@Aspect
1921
@Component
2022
@Slf4j
2123
@RequiredArgsConstructor
2224
public class MemberLockAspect {
2325

24-
private final StringRedisTemplate redisTemplate;
26+
private final RedissonClient redissonClient;
2527
private final String MEMBER_LOCK_KEY = "memberLock";
26-
private final Integer MEMBER_LOCK_TTL = 5;
27-
2828
@Pointcut("@annotation(swm_nm.morandi.aop.annotation.MemberLock)")
2929
public void memberLockPointcut() {
3030
}
3131

3232
@Around("memberLockPointcut()")
33-
public Object MEMBERLock(ProceedingJoinPoint joinPoint) throws Throwable {
33+
public Object MEMBERLock(ProceedingJoinPoint joinPoint) throws Throwable {
3434
Long memberId = SecurityUtils.getCurrentMemberId();
35-
String memberLockKey = String.format("%s:%d", MEMBER_LOCK_KEY,memberId);
35+
String memberLockKey = String.format("%s:%d", MEMBER_LOCK_KEY, memberId);
36+
RLock lock = redissonClient.getLock(memberLockKey);
3637
boolean locked = false;
3738
try {
38-
if (tryLock(memberLockKey)) {
39-
locked = true;
40-
return joinPoint.proceed();
41-
} else {
39+
locked = lock.tryLock(2, 5, TimeUnit.SECONDS);
40+
if (!locked) {
4241
throw new MorandiException(LockErrorCode.MEMBER_LOCKED);
4342
}
43+
return joinPoint.proceed();
44+
} catch (InterruptedException e) {
45+
throw new MorandiException(LockErrorCode.INTERRUPT_ERROR);
4446
} finally {
45-
if(locked) {
46-
unlock(memberLockKey);
47+
if (locked) {
48+
lock.unlock();
4749
}
4850
}
4951
}
50-
51-
private Boolean tryLock(String key) {
52-
return redisTemplate.opsForValue().setIfAbsent(key, "locked", MEMBER_LOCK_TTL, TimeUnit.SECONDS);
53-
}
54-
55-
private void unlock(String key) {
56-
redisTemplate.delete(key);
57-
}
58-
59-
}
60-
52+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package swm_nm.morandi.config.async;
2+
3+
import org.springframework.context.annotation.Bean;
4+
import org.springframework.context.annotation.Configuration;
5+
import org.springframework.core.task.TaskExecutor;
6+
import org.springframework.scheduling.annotation.EnableAsync;
7+
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
8+
9+
@Configuration
10+
@EnableAsync
11+
public class AsyncConfig {
12+
13+
@Bean(name = "taskExecutor")
14+
public TaskExecutor taskExecutor() {
15+
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
16+
executor.setCorePoolSize(5); // 기본 스레드 풀 크기
17+
executor.setMaxPoolSize(10); // 최대 스레드 풀 크기
18+
executor.setQueueCapacity(25); // 큐의 용량
19+
executor.setThreadNamePrefix("Async-");
20+
executor.initialize();
21+
return executor;
22+
}
23+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
package swm_nm.morandi.domain.codeSubmit.controller;
2+
3+
import lombok.RequiredArgsConstructor;
4+
import org.springframework.web.bind.annotation.PathVariable;
5+
import org.springframework.web.bind.annotation.PostMapping;
6+
import org.springframework.web.bind.annotation.RestController;
7+
import swm_nm.morandi.domain.codeSubmit.service.TestService;
8+
9+
@RestController
10+
@RequiredArgsConstructor
11+
public class TestController {
12+
13+
private final TestService testService;
14+
15+
@PostMapping("/hello-world/{value}")
16+
public String getData(@PathVariable("value") Integer value) throws InterruptedException {
17+
return testService.test(value);
18+
}
19+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package swm_nm.morandi.domain.codeSubmit.service;
2+
3+
import lombok.extern.slf4j.Slf4j;
4+
import org.springframework.stereotype.Service;
5+
6+
@Service
7+
@Slf4j
8+
public class TestService {
9+
public String test(int value) throws InterruptedException {
10+
System.out.println("Response Message SSEID : " + 1 + " Count : " + value);
11+
return "hello";
12+
}
13+
}

src/main/java/swm_nm/morandi/global/exception/errorcode/LockErrorCode.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,9 @@
99
@RequiredArgsConstructor
1010
@Getter
1111
public enum LockErrorCode implements ErrorCode {
12-
MEMBER_LOCKED(HttpStatus.CONFLICT, "한 번에 여러 번의 요청을 받을 수 없습니다. 잠시만 기다려주세요.");
12+
MEMBER_LOCKED(HttpStatus.CONFLICT, "한 번에 여러 번의 요청을 받을 수 없습니다. 잠시만 기다려주세요."),
13+
INTERRUPT_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "인터럽트 예외가 발생했습니다.");
1314

1415
private final HttpStatus httpStatus;
1516
private final String message;
16-
17-
1817
}

0 commit comments

Comments
 (0)