본문 바로가기
server

Redisson

by Younji! 2019. 6. 26.

현재 운영하고 있는 서비스 특성 상, 여러 개의 트랜잭션이 연거푸 요청될 수 있어 주문/결제 처럼 트랜잭션이 동기화 처리가 필요할 때 사용하기 위해 분산 락을 사용하기로 합니다. 그 중에 Redisson을 사용합니다.

Redisson은 인메모리 데이터 그리드 기능을 가진 Redis Java Client 입니다. 데이터 모델링과 어플리케이션 로직에 집중할 수 있게 도와줍니다. 여러 레디슨 노드를 활용해서 분산환경에서 핸들링 할 수 있습니다. Redisson을 도입하게 된 큰 이유는 Redis Cluster 설정과 지원이 간단하고 용이하다는 점이 있겠습니다.

https://github.com/redisson/redisson/wiki/1.-Overview

Lock을 얻기위한 WaitTime, LeaseTime

lock을 얻기 위한 대기시간(=waitTime)과 락을 임대할 시간(=leaseTime)을 설정해서 중복 요청을 막기 위해 아래와 같이 확인할 수 있습니다.

public void lock(String key, long waitTime, long leaseTime) {
       RLock lock = redissonClient.getLock(key);
       if (lock.tryLock(waitTime, leaseTime, TimeUnit.MILLISECONDS)) {
        //do something..
       }
}

tryLock의 코드를 살펴보면 아래와 같습니다. waitTime이 0이 아닐때까지 lock을 얻기 위해 시도하다 해당 범위를 벗어나면 획득 처리 실패를 하게 됩니다. Pubsub으로 구현되어 있는데, 보게 되면 스레드ID로 subscriber를 구성하고 완료되었으면 이를 해제합니다. 

RFuture subscribeFuture = subscribe(threadId);
.....
try {
    if (time <= 0) {
        acquireFailed(threadId);
        return false;
    }
    ttl = tryAcquire(leaseTime, unit, threadId);
    ....
} finally {
    unsubscribe(subscribeFuture, threadId);
}

 

테스트 케이스

간단한 테스트로 락이 제대로 잡히는지 확인해봅니다. 

@Inject
  private RedissonClient redissonClient;
  private Exception exception;

  private final CountDownLatch latch = new CountDownLatch(2);

  @Test
  public void syncTest() {
      List threads = IntStream.range(0, 2)
          .mapToObj(i -> new Thread1())
          .map(Thread::new).collect(Collectors.toList());

      threads.forEach(Thread::start);

      threads.forEach(a -> {
          try {
              a.join();
          } catch (Exception ex) {
              throw new RuntimeException(ex);
          }
      });

      assertThat(exception.getClass()).isEqualTo(IllegalMonitorStateException.class);
  }

  private void lock() throws Exception {
      final String lockName = "test-lock";
      RLock rLock = redissonClient.getLock(lockName);
      if (rLock.tryLock(0, 1000, TimeUnit.MILLISECONDS)) {
          System.out.println("Do it Something");
      }

      rLock.unlock();
  }

  public class Thread1 implements Runnable {
      @Override
      public void run() {
          try {
              latch.countDown();
              latch.await();

              lock();
          } catch (Exception ex) {
              exception = ex;
          }
      }
  }

 

운영 중에 트랜잭션이 묶인 메소드에 락을 잡았으나, 락이 해제되자마자 유입되는 Request에 동시성 제어가 의도대로 동작하지 않는 이슈가 있었습니다. 

다음 포스팅엔 해당 내용과 해결방안에 대해 짤막하게 기록합니다.

 

참고

https://hyperconnect.github.io/2019/11/15/redis-distributed-lock-1.html

https://github.com/redisson/redisson

'server' 카테고리의 다른 글

어째서 엔티티가 계속 업데이트 되는가..  (0) 2019.06.28
fluentd와 함께하는 검색 데이터 수집  (0) 2019.06.26
ModelMapper  (0) 2019.06.25
Lambda@Edge를 활용한 이미지 리사이징  (1) 2019.03.24
WAS 속도 느림 현상  (0) 2017.02.01

댓글