-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Browse files
Browse the repository at this point in the history
refactor: 분산락 Redisson으로 적용 및 AOP로 적용
- Loading branch information
Showing
11 changed files
with
198 additions
and
72 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
58 changes: 0 additions & 58 deletions
58
src/main/java/com/dnd/jjakkak/domain/schedule/facade/ScheduleFacade.java
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
35 changes: 35 additions & 0 deletions
35
src/main/java/com/dnd/jjakkak/global/annotation/redisson/RedissonLock.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
package com.dnd.jjakkak.global.annotation.redisson; | ||
|
||
import java.lang.annotation.ElementType; | ||
import java.lang.annotation.Retention; | ||
import java.lang.annotation.RetentionPolicy; | ||
import java.lang.annotation.Target; | ||
|
||
/** | ||
* Redisson AOP 어노테이션입니다. | ||
* | ||
* <li>적용하려는 메소드에 Transactional을 지우고 RedissonLock을 적용하면 됩니다.</li> | ||
* <li>ex) @RedissonLock(value = "#meetingUuid")</li> | ||
* | ||
* @author 류태웅 | ||
* @version 2024. 10. 15. | ||
*/ | ||
@Target({ElementType.METHOD, ElementType.TYPE}) | ||
@Retention(RetentionPolicy.RUNTIME) | ||
public @interface RedissonLock { | ||
|
||
/** | ||
* Lock Key | ||
*/ | ||
String key(); | ||
|
||
/*** | ||
* Lock 획득을 시도하는 최대 시간 (ms) | ||
*/ | ||
long waitTime() default 5000L; | ||
|
||
/** | ||
* 락을 획득한 후, 점유하는 최대 시간 | ||
*/ | ||
long leaseTime() default 3000L; | ||
} |
62 changes: 62 additions & 0 deletions
62
src/main/java/com/dnd/jjakkak/global/annotation/redisson/RedissonLockAspect.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,62 @@ | ||
package com.dnd.jjakkak.global.annotation.redisson; | ||
|
||
import com.dnd.jjakkak.global.util.CustomSpringELParser; | ||
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.reflect.MethodSignature; | ||
import org.redisson.api.RLock; | ||
import org.redisson.api.RedissonClient; | ||
import org.springframework.stereotype.Component; | ||
|
||
import java.lang.reflect.Method; | ||
import java.util.concurrent.TimeUnit; | ||
|
||
/** | ||
* Redisson Lock Aspect 클래스입니다. | ||
* | ||
* @author 류태웅, 정승조 | ||
* @version 2024. 10. 15. | ||
*/ | ||
@Slf4j | ||
@Aspect | ||
@Component | ||
@RequiredArgsConstructor | ||
public class RedissonLockAspect { | ||
private final RedissonClient redissonClient; | ||
private final TransactionAspect transactionAspect; | ||
|
||
/** | ||
* '@RedissonLock' 어노테이션을 사용한 메서드에 대한 Advice. | ||
*/ | ||
@Around("@annotation(com.dnd.jjakkak.global.annotation.redisson.RedissonLock)") | ||
public Object redissonLock(ProceedingJoinPoint joinPoint) throws Throwable { | ||
MethodSignature signature = (MethodSignature) joinPoint.getSignature(); | ||
Method method = signature.getMethod(); | ||
RedissonLock annotation = method.getAnnotation(RedissonLock.class); | ||
|
||
// todo: prefix 변경 필요 (현재는 모임 관련 로직에서만 사용) | ||
String lockKey = "meeting-" + CustomSpringELParser.getDynamicValue(signature.getParameterNames(), joinPoint.getArgs(), annotation.key()); | ||
|
||
RLock lock = redissonClient.getLock(lockKey); | ||
try { | ||
boolean lockable = lock.tryLock(annotation.waitTime(), annotation.leaseTime(), TimeUnit.MILLISECONDS); | ||
|
||
if (!lockable) { | ||
throw new IllegalArgumentException(); | ||
} | ||
|
||
return transactionAspect.proceed(joinPoint); | ||
} catch (InterruptedException e) { | ||
// todo : 재시도 로직이 필요한가? | ||
log.error("Redisson Lock Exception", e); | ||
throw new RuntimeException(); | ||
} finally { | ||
if (lock != null && lock.isHeldByCurrentThread()) { | ||
lock.unlock(); | ||
} | ||
} | ||
} | ||
} |
28 changes: 28 additions & 0 deletions
28
src/main/java/com/dnd/jjakkak/global/annotation/redisson/TransactionAspect.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
package com.dnd.jjakkak.global.annotation.redisson; | ||
|
||
import org.aspectj.lang.ProceedingJoinPoint; | ||
import org.springframework.stereotype.Component; | ||
import org.springframework.transaction.annotation.Propagation; | ||
import org.springframework.transaction.annotation.Transactional; | ||
|
||
/** | ||
* 트랜잭션 분리를 위한 TransactionAspect 클래스입니다. | ||
* | ||
* @author 류태웅 | ||
* @version 2024. 10. 15. | ||
*/ | ||
|
||
@Component | ||
public class TransactionAspect { | ||
|
||
/** | ||
* 트랜잭션을 분리하기 위한 메서드 (다른 트랜잭션과 분리) | ||
* | ||
* <li> REQUIRES_NEW 를 통해 별도의 트랜잭션으로 관리한다.</li> | ||
* <li> timeout 시간 내에 수행이 되어야 한다. (아니면 롤백) </li> | ||
*/ | ||
@Transactional(propagation = Propagation.REQUIRES_NEW, timeout = 2) | ||
public Object proceed(final ProceedingJoinPoint joinPoint) throws Throwable { | ||
return joinPoint.proceed(); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
33 changes: 33 additions & 0 deletions
33
src/main/java/com/dnd/jjakkak/global/config/redis/RedissonConfig.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
package com.dnd.jjakkak.global.config.redis; | ||
|
||
import lombok.RequiredArgsConstructor; | ||
import org.redisson.Redisson; | ||
import org.redisson.api.RedissonClient; | ||
import org.redisson.config.Config; | ||
import org.springframework.boot.autoconfigure.data.redis.RedisProperties; | ||
import org.springframework.context.annotation.Bean; | ||
import org.springframework.context.annotation.Configuration; | ||
|
||
/** | ||
* Redisson 설정 클래스입니다. | ||
* | ||
* @author 류태웅 | ||
* @version 2024. 10. 15. | ||
*/ | ||
@Configuration | ||
@RequiredArgsConstructor | ||
public class RedissonConfig { | ||
|
||
private static final String REDISSON_HOST_PREFIX = "redis://"; | ||
private final RedisProperties redisProperties; | ||
|
||
/** | ||
* Redisson Client 등록 메서드. | ||
*/ | ||
@Bean | ||
public RedissonClient redissonClient() { | ||
Config config = new Config(); | ||
config.useSingleServer().setAddress(REDISSON_HOST_PREFIX + redisProperties.getHost() + ":" + redisProperties.getPort()); | ||
return Redisson.create(config); | ||
} | ||
} |
28 changes: 28 additions & 0 deletions
28
src/main/java/com/dnd/jjakkak/global/util/CustomSpringELParser.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
package com.dnd.jjakkak.global.util; | ||
|
||
import org.springframework.expression.spel.standard.SpelExpressionParser; | ||
import org.springframework.expression.spel.support.StandardEvaluationContext; | ||
|
||
/** | ||
* 어노테이션에 작성된 Spring EL 표현식을 파싱하는 클래스입니다. | ||
* | ||
* @author 류태웅 | ||
* @version 2024. 10. 15. | ||
*/ | ||
public class CustomSpringELParser { | ||
|
||
private CustomSpringELParser() { | ||
throw new IllegalStateException("Utility class"); | ||
} | ||
|
||
public static Object getDynamicValue(String[] parameterNames, Object[] args, String key) { | ||
SpelExpressionParser parser = new SpelExpressionParser(); | ||
StandardEvaluationContext context = new StandardEvaluationContext(); | ||
|
||
for (int i = 0; i < parameterNames.length; i++) { | ||
context.setVariable(parameterNames[i], args[i]); | ||
} | ||
|
||
return parser.parseExpression(key).getValue(context, Object.class); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters