-
유저의 토너먼트 참여를 위한 그룹 만들기
- 최대 50명의 유저가 한 그룹에 참여되도록 한다.
-
요구사항을 만족하기 위해 1 그룹당 최대 50명만 참여가 될 수 있도록 락을 도입
-
인증은 임시로 Authorization 에 uid (userId) 를 넣어 사용
- https://github.com/redisson/redisson/wiki/8.-Distributed-locks-and-synchronizers
- pubsub 기능을 사용하여 락이 해제될 때마다 subscribe 하는 클라이언트들에게 락 획득을 시도해도 된다고 알려줍니다.
- timeout 이 지나면 최종적으로
false
를 반환하고 락 획득에 실패했음을 알려줍니다. - Lua Script를 사용하여 command 를 한번에 redis에 전송하여 atomic을 보장하면서 동시에 성능을 높이고 있습니다.
- 임계 구역에 진입이 불가능할 때 진입이 가능할 때까지 루프를 돌면서 재시도하는 방식으로 구현된 락을 가리킨다
1642681263.180637 [0 127.0.0.1:2542] "EVAL" "if (redis.call('exists', KEYS[1]) == 0) then redis.call('hincrby', KEYS[1], ARGV[2], 1); redis.call('pexpire', KEYS[1], ARGV[1]); return nil; end; if (redis.call('hexists', KEYS[1], ARGV[2]) == 1) then redis.call('hincrby', KEYS[1], ARGV[2], 1); redis.call('pexpire', KEYS[1], ARGV[1]); return nil; end; return redis.call('pttl', KEYS[1]);" "1" "TournamentGroup" "30000" "9563f680-0c67-4857-a1c8-45917cbd5bc7:1"
1642681263.180725 [0 lua] "exists" "TournamentGroup"
1642681263.180736 [0 lua] "hincrby" "TournamentGroup" "9563f680-0c67-4857-a1c8-45917cbd5bc7:1" "1"
1642681263.180751 [0 lua] "pexpire" "TournamentGroup" "30000"
1642681263.314685 [0 127.0.0.1:2540] "EVAL" "if (redis.call('hexists', KEYS[1], ARGV[3]) == 0) then return nil;end; local counter = redis.call('hincrby', KEYS[1], ARGV[3], -1); if (counter > 0) then redis.call('pexpire', KEYS[1], ARGV[2]); return 0; else redis.call('del', KEYS[1]); redis.call('publish', KEYS[2], ARGV[1]); return 1; end; return nil;" "2" "TournamentGroup" "redisson_lock__channel:{TournamentGroup}" "0" "30000" "9563f680-0c67-4857-a1c8-45917cbd5bc7:1"
1642681263.314784 [0 lua] "hexists" "TournamentGroup" "9563f680-0c67-4857-a1c8-45917cbd5bc7:1"
1642681263.314801 [0 lua] "hincrby" "TournamentGroup" "9563f680-0c67-4857-a1c8-45917cbd5bc7:1" "-1"
1642681263.314810 [0 lua] "del" "TournamentGroup"
1642681263.314817 [0 lua] "publish" "redisson_lock__channel:{TournamentGroup}" "0"
public interface Lock {
boolean tryLock(long time, TimeUnit unit) throws InterruptedException;
}
public class LockTest {
public static void main(String[] args) {
RLock lock = redissonClient.getLock(name);
if (lock.tryLock(timeout.toMillis(), TimeUnit.MILLISECONDS)) {
try {
// logic
} catch (Throwable t) {
// error
} finally {
lock.unlock();
}
} else {
throw new IllegalStateException("redisson lock not available");
}
}
}
- Redisson 으로 lock 을 간편하게 사용하기 위해 AOP를 도입
RedissonLock
annotation을 메소드에 설정하게 되면RedissonLockAdvice
AOP 가 동작되도록 설정- MySQL 8.0.x 이상 필요