작년 가을쯤 현재 재직중인 회사에서 선착순 쿠폰을 발급하는 이벤트 개발을 맡게되었습니다.
선착순 쿠폰 발급 이벤트를 개발하면서 학습한 내용을 기록하고자 합니다.
- 특정 기간에 다운 가능한 쿠폰이 존재하며, 총지급 수량은 한정되어 있다.
- 쿠폰 수량은 정해진 양을 초과해서는 안된다.
- 쿠폰은 1인당 1장만 발급가능하다.
이번 선착순 쿠폰 발급은 게릴라 세일 구좌를 차지한 셀러들의 쿠폰을 특정 시간이 도래하면 다운 받을 수 있습니다.
이때, 다수의 요청이 발생하게 된다면 정해진 수량의 쿠폰보다 많은 양의 쿠폰이 발급될 수 있다.
위 요구사항을 만족하기위해 분산락을 통해 동시성 이슈를 해결하고 Redis를 활용하기로 하였다.
동시성을 해결하기 위한 방안으로는 여러가지가 있습니다.
- Java Synchronized 사용
- DB 제어
- Pessimistic Lock(비관적 락) 활용
- Optimistic Lock(낙관적 락) 활용
- Named Lock 활용
- Redis 활용
- Redis Lettuce Client 활용
- Redis Redission Client 활용
분산락이란, 여러 대의 서버나 프로세스 간에 데이터 또는 자원을 안전하게 동기화하기 위한 매커니즘이다.
DB 등 공통된 저장소를 이용하여 자원이 사용 중인지를 체크하기에 전체 서버에서 동기화된 처리가 가능해진다.
이번 개발의 목표는 쿠폰 발급의 동시성 문제뿐만 아니라 다양한 기획에서도 사용할 수 있도록 공통화된 분산락을 구현하는 것이 목표였다.
분산락을 구현한 덕분에 블로그 포스팅 날짜 기준 서비스 내 여러 기획에서도 분산락을 문제 없이 사용중에 있다.
Jedis, Lettuce, Redisson은 모두 자바 레디스 클라이언트이다.
Jedis는 동기식 blocking I/O를 사용한다.
Lettuce, Redisson은 는 비동기 non-blocking I/O를 사용한다.
그래서 Jedis를 제외한 Lettuce, Redisson은 분산락을 어떻게 구현했는지 알아보도록 하자
위에서도 언급했듯이 동시성을 해결하기 위한 방안은 여러가지가 있으며 그에 따른 장/단점이 존재한다.
Redis의 Jedis는 Lettus에 비해 압도적으로 성능차이가 발생한다.
자세한 내용은 이동욱님 블로그를 참고하세요.
tryLock을 성공하였더라도 애플리케이션 이슈가 발생하면 다른 애플리케이션은 무한정 대기상태가 되어버린다.락이 만료되도록 구현해야 한다. 하지만, automic한 명령어 수행을 위해 setnx 로 묶여있기에 expire time을 지정할 수 없기에 문제 해결하기가 어렵다.매 50ms마다 락 시도. 락 획득에는 300ms. (300ms/50ms) 총 6회 시도. 첫 번째 요청을 제외한 5번 시도를 99개의 클라이언트로부터 요청이 들어옴. 99*5 = 495회아래 내용에서 설명)이 지나기 전까지 락 해제 메시지를 기다리게 됩니다.tryLock 메소드에 타임아웃을 명시하도록 되어있다.위에서 언급한 타임아웃), 두 번째 파라미터는 락이 만료되는 시간이다.public boolean tryLock(long waitTime, long leaseTime, TimeUnit unit) throws InterruptedException
락 획득 가능 여부 확인, 락 획득은 atomic해야 한다.pub/sub 두 명령은 atomic해야 한다.참고
https://hyperconnect.github.io/2019/11/15/redis-distributed-lock-1.html
https://jojoldu.tistory.com/418