인프런 커뮤니티 질문&답변

avenue님의 프로필 이미지
avenue

작성한 질문수

재고시스템으로 알아보는 동시성이슈 해결방법

Redisson Client Lock

해결된 질문

작성

·

661

·

수정됨

1

public void buy(String ticketName) throws InterruptedException {
    RLock lock = redissonClient.getLock(ticketName);

    try {
        boolean available = lock.tryLock(5, 1, TimeUnit.SECONDS);
        if (!available) {
            return;
        }

        ticketServiceWithRedisRedissonClientLock.buy(ticketName);
    } finally {
        if (lock.isLocked() && lock.isHeldByCurrentThread()) {
            lock.unlock();
        }
    }
}

이 구조에서 tryLock에 대해서 5초동안 Lock 획득을 위해서 대기하고 Lock을 1초동안 점유하고 release하는 형식으로 알고있는데 만약에 쓰레드가 5초 동안 대기를 하더라도 최종적으로 Lock을 얻지 못한다면 return이 됨으로써 buy로직으로 못들어가는거 아닌가요??

 

만약에 반드시 Lock을 얻고 buy로 들어가야만 하는 경우 Lock 재획득에 대한 로직을 따로 구현해야 하는건가요. 아니면 tryLock의 재시도에 대해서 제가 모르는 부분이 있는건가요??

 

public void buy(String ticketName) throws InterruptedException {
    final RLock lock = redissonClient.getLock(ticketName);
    final int maxRetryCount = 10;
    final int retryIntervalMillis = 1000;

    try {
        int retryCount = 0;

        boolean lockAcquired = lock.tryLock(5, 1, TimeUnit.SECONDS);
        while (!lockAcquired && retryCount < maxRetryCount) {
            log.info(
                    "--> Thread [{}] Redis Distributed Lock (Redisson Client) 획득 대기",
                    Thread.currentThread().getName()
            );
            Thread.sleep(retryIntervalMillis);
            lockAcquired = lock.tryLock(5, 1, TimeUnit.SECONDS);
            retryCount++;
        }

        if (!lockAcquired) {
            log.info(
                    "--> Thread [{}] Redis Distributed Lock (Redisson Client) 획득 실패",
                    Thread.currentThread().getName()
            );
            return;
        }

        ticketServiceWithRedisRedissonClientLock.buy(ticketName);
    } finally {
        if (lock.isLocked() && lock.isHeldByCurrentThread()) {
            lock.unlock();
        }
    }
}

이런 방식으로 최대 재시도 횟수와 재시도 간 간격을 통해서 waitTime동안 Lock을 못얻을 경우 Lock자체를 다시 얻는 방식을 구현해보았고 waitTime=1, unit=ms로 변경하고 테스트했을 경우 정상적으로 retry가 됨을 확인했습니다.

 

이런 구조가 최선일까요??

아니면 더 나은 방식이 있을까요?

 

답변 2

1

최상용님의 프로필 이미지
최상용
지식공유자

avenue 님 안녕하세요.
말씀하신 내용과 같이 waitTime 만큼 기다렸다가 획득에 실패하면 return 을 하게됩니다.

무조건 획득을 해야하는 방식이라면 avenue 님께서 말씀해주신 방법대로 진행을 할 것 같습니다.
감사합니다.

0

안녕하세요, 인프런 AI 인턴이에요.

tryLock 메소드는 매개변수로 받은 대기 시간 안에 Lock을 획득할 수 없다면 false를 반환하여 리턴합니다. 그렇다면 buy 메소드에서 lock.tryLock(5, 1, TimeUnit.SECONDS) 대기 시간이 5초이므로 5초 안에 Lock을 획득하지 못하면 false를 반환합니다. 이 경우 buy 메소드에서는 해당 티켓에 대한 Lock을 획득하지 못하게 되어 ticketServiceWithRedisRedissonClientLock.buy 메소드를 실행하지 않고 바로 return을 하게 됩니다.

만약에 반드시 Lock을 얻고 buy 메소드 실행이 필요하다면, retry 로직을 추가하여 Lock 획득이 가능할 때까지 tryLock 메소드를 반복 호출하는 방식으로 처리할 수 있습니다. 물론 이 경우에도 최대 대기 시간과 재시도 시간 등을 고려하여 구현해야 합니다.

참고로 Redisson의 RLock 객체는 Redis에서 제공하는 분산 Locking 기능을 사용하므로, 여러 인스턴스에서 동시에 사용해도 정확하게 Locking이 동작합니다.

도움이 되었길 바랍니다. 감사해요 :)

avenue님의 프로필 이미지
avenue

작성한 질문수

질문하기