-
Notifications
You must be signed in to change notification settings - Fork 3
[feat] gameStart 및 handlePlayerReady 레디스 분산락 기반 동시성 제어 적용 #123
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 22 commits
Commits
Show all changes
25 commits
Select commit
Hold shift + click to select a range
b8cbf8d
:wrench: chore: redisson v3.50.0 의존성 추가
LimKangHyun 4ad1312
:sparkles: feat: Redis 분산락 AOP 기반 구현
LimKangHyun 35fe39f
chore: Java 스타일 수정
ce864dd
:sparkles: feat: gameStart와 handlePlayerReady에 분산 락 적용
LimKangHyun 7f83ba9
chore: Java 스타일 수정
a13075b
:white_check_mark: test: DistributedLockAspect 단위 테스트 추가
LimKangHyun e764b1a
chore: Java 스타일 수정
40e0b3a
:white_check_mark: test: 분산 락 통합 테스트 추가
LimKangHyun 66eb5e2
chore: Java 스타일 수정
c7da054
:white_check_mark: test: RedissonTestContainerConfig 추가
LimKangHyun c06e8ab
chore: Java 스타일 수정
f1a484c
:bug: fix: RedissonConfig host, port 경로 재설정
LimKangHyun 00eb06b
:white_check_mark: test: RoomServiceTests에 timeService 추가
LimKangHyun 827bdb8
chore: Java 스타일 수정
ffec34d
:recycle: chore: Redis 기본값 application.yml에 적용
LimKangHyun 8b27655
:wrench: chore: application.yml 원복
LimKangHyun a37ad68
:bug: fix: redis host, port 설정이 되어 있지 않아 발생하는 오류 수정
LimKangHyun 08a71c6
:bug: fix: redis host, port 설정이 되어 있지 않아 발생하는 오류 수정
LimKangHyun e7d524a
chore: Java 스타일 수정
447159a
:wastebasket: remove: redissonConfig 파일 삭제
LimKangHyun 4fd7737
:wastebasket: remove: test application.yml 파일 빈 오버라이딩 허용 삭제
LimKangHyun a0af97b
:bug: fix: git conflict 해결
LimKangHyun d65fd83
:wastebasket: remove: 플레이어 로그 삭제
LimKangHyun a30ff90
chore: Java 스타일 수정
f79c272
:recycle: refactor: 테스트 Order 제거
LimKangHyun File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or 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 hidden or 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 hidden or 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 hidden or 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 hidden or 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
24 changes: 24 additions & 0 deletions
24
backend/src/main/java/io/f1/backend/global/lock/CustomSpringELParser.java
This file contains hidden or 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,24 @@ | ||
| package io.f1.backend.global.lock; | ||
|
|
||
| import org.springframework.expression.ExpressionParser; | ||
| import org.springframework.expression.spel.standard.SpelExpressionParser; | ||
| import org.springframework.expression.spel.support.StandardEvaluationContext; | ||
|
|
||
| public class CustomSpringELParser { | ||
|
|
||
| private CustomSpringELParser() {} | ||
|
|
||
| public static Object getDynamicValue( | ||
| String[] parameterNames, Object[] args, String keyExpression) { | ||
|
|
||
| ExpressionParser parser = new SpelExpressionParser(); | ||
|
|
||
| StandardEvaluationContext context = new StandardEvaluationContext(); | ||
|
|
||
| for (int i = 0; i < parameterNames.length; i++) { | ||
| context.setVariable(parameterNames[i], args[i]); | ||
| } | ||
|
|
||
| return parser.parseExpression(keyExpression).getValue(context, Object.class); | ||
| } | ||
| } |
27 changes: 27 additions & 0 deletions
27
backend/src/main/java/io/f1/backend/global/lock/DistributedLock.java
This file contains hidden or 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,27 @@ | ||
| package io.f1.backend.global.lock; | ||
|
|
||
| import java.lang.annotation.Documented; | ||
| import java.lang.annotation.ElementType; | ||
| import java.lang.annotation.Retention; | ||
| import java.lang.annotation.RetentionPolicy; | ||
| import java.lang.annotation.Target; | ||
| import java.util.concurrent.TimeUnit; | ||
|
|
||
| @Target(ElementType.METHOD) | ||
| @Retention(RetentionPolicy.RUNTIME) | ||
| @Documented | ||
| public @interface DistributedLock { | ||
|
|
||
| String prefix(); | ||
|
|
||
| String key(); | ||
|
|
||
| // 시간단위를 초로 변경 | ||
| TimeUnit timeUnit() default TimeUnit.SECONDS; | ||
|
|
||
| // 락 점유를 위한 대기 시간 | ||
| long waitTime() default 5L; | ||
|
|
||
| // 락 점유 시간 | ||
| long leaseTime() default 3L; | ||
| } |
74 changes: 74 additions & 0 deletions
74
backend/src/main/java/io/f1/backend/global/lock/DistributedLockAspect.java
This file contains hidden or 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,74 @@ | ||
| package io.f1.backend.global.lock; | ||
|
|
||
| import io.f1.backend.global.exception.CustomException; | ||
| import io.f1.backend.global.exception.errorcode.CommonErrorCode; | ||
|
|
||
| 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; | ||
|
|
||
| @Slf4j | ||
| @Aspect | ||
| @Component | ||
| @RequiredArgsConstructor | ||
| public class DistributedLockAspect { | ||
|
|
||
| private static final String LOCK_KEY_FORMAT = "lock:%s:{%s}"; | ||
|
|
||
| private final RedissonClient redissonClient; | ||
|
|
||
| @Around("@annotation(distributedLock)") | ||
| public Object lock(ProceedingJoinPoint joinPoint, DistributedLock distributedLock) | ||
| throws Throwable { | ||
|
|
||
| String key = getLockKey(joinPoint, distributedLock); | ||
|
|
||
| RLock rlock = redissonClient.getLock(key); | ||
|
|
||
| boolean acquired = false; | ||
| try { | ||
| acquired = | ||
| rlock.tryLock( | ||
| distributedLock.waitTime(), | ||
| distributedLock.leaseTime(), | ||
| distributedLock.timeUnit()); | ||
|
|
||
| if (!acquired) { | ||
| log.warn("[DistributedLock] Lock acquisition failed: {}", key); | ||
| throw new CustomException(CommonErrorCode.LOCK_ACQUISITION_FAILED); | ||
| } | ||
| log.info("[DistributedLock] Lock acquired: {}", key); | ||
|
|
||
| return joinPoint.proceed(); | ||
| } catch (InterruptedException e) { | ||
| Thread.currentThread().interrupt(); | ||
| throw e; | ||
| } finally { | ||
| if (acquired && rlock.isHeldByCurrentThread()) { | ||
| rlock.unlock(); | ||
| log.info("[DistributedLock] Lock released: {}", key); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| private String getLockKey(ProceedingJoinPoint joinPoint, DistributedLock lockAnnotation) { | ||
| MethodSignature signature = (MethodSignature) joinPoint.getSignature(); | ||
|
|
||
| String keyExpr = lockAnnotation.key(); | ||
| String prefix = lockAnnotation.prefix(); | ||
|
|
||
| Object keyValueObj = | ||
| CustomSpringELParser.getDynamicValue( | ||
| signature.getParameterNames(), joinPoint.getArgs(), keyExpr); | ||
| String keyValue = String.valueOf(keyValueObj); | ||
|
|
||
| return String.format(LOCK_KEY_FORMAT, prefix, keyValue); | ||
| } | ||
| } |
This file contains hidden or 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 hidden or 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,2 @@ | ||
| singleServerConfig: | ||
| address: "redis://${REDIS_HOST}:${REDIS_PORT}" |
This file contains hidden or 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
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
디버깅용 로그인가요 ㅎㅎ
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
넵 gameStart에 락을 안걸었을 때, gameStart가 플레이어의 레디를 전부 읽고나서, handlePlayerReady가 동작해서 실제로 false인 상태로 게임이 시작되는지 보려고 적어놨었습니다!
지금은 필요없을것 같은데 로그 정리해서 커밋해 놓았습니다!