본문 바로가기
SpringBoot

스프링의 RedisLockRegistry를 활용한 제한된 상품의 동시성 제어 및 주문 처리

by ByteBridge 2023. 10. 9.
반응형

개요

이 포스팅에서는 스프링의 RedisLockRegistry를 활용하여 제한된 상품의 주문 처리에 대한 동시성 제어를 다루어 보겠습니다. 사용자가 동일한 제한된 상품을 구매할 때, 여러 주문 서비스 인스턴스에서 동시성 제어를 해야 할 필요가 있습니다. 이 때 RedisLockRegistry가 큰 역할을 합니다.

설정

의존성 추가

id 'org.springframework.boot' version '3.1.4'
id 'io.spring.dependency-management' version '1.1.3'

먼저, 필요한 의존성을 추가합니다.

implementation 'org.springframework.integration:spring-integration-redis'

RedisLockRegistry 설정

RedisLockRegistry를 스프링 빈으로 등록합니다.

@Configuration
public class RedisConfig {

    @Bean
    public RedisConnectionFactory redisConnectionFactory() {
        return new LettuceConnectionFactory("localhost", 6379);
    }

    @Bean
    public RedisLockRegistry redisLockRegistry(RedisConnectionFactory redisConnectionFactory) {
        return new RedisLockRegistry(redisConnectionFactory, "locks");
    }
}

 

주문 처리 로직

주문 처리 로직 개요

  1. 사용자는 한 종류의 제한된 상품만 주문할 수 있습니다.
  2. 주문을 시작하면 해당 주문은 Redis 분산 락으로 보호되어야 합니다.
  3. 결제가 완료되면 락은 해제됩니다.
  4. 만약 주문이 취소되거나 결제가 실패하면 락 역시 해제되어야 합니다.
  5. 일정 시간 내에 결제가 완료되지 않는다면 락은 자동으로 해제됩니다.

주문 서비스 구현

아래는 위 로직을 기반으로 한 주문 서비스의 간략한 구현입니다.

@Service
public class OrderService {

    @Autowired
    private RedisLockRegistry redisLockRegistry;

    @Autowired
    private OrderRepository orderRepository;

    public boolean processOrder(OrderRequest orderRequest, User user) {
        String lockKey = "product:" + orderRequest.getProductId();
        Lock lock = redisLockRegistry.obtain(lockKey);

        try {
            // 락 획득 시도 (예: 최대 5초 동안 기다림)
            if (lock.tryLock(5, TimeUnit.SECONDS)) {
                // 이미 주문한 상품인지 확인
                if (hasAlreadyOrdered(user, orderRequest.getProductId())) {
                    return false;
                }

                // 주문 로직 처리...
                orderRepository.save(new Order(user, orderRequest.getProductId(), ...));

                // 결제 로직 처리...
                boolean paymentSuccess = paymentProcess(orderRequest);
                
                return paymentSuccess;
            } else {
                // 락 획득 실패 (다른 사용자가 주문 중인 경우)
                return false;
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
            // 예외 처리 로직...
            return false;
        } finally {
            // 무조건 락 해제
            if (lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
    }

    private boolean hasAlreadyOrdered(User user, String productId) {
        // 사용자가 이미 주문한 상품인지 확인하는 로직...
    }

    private boolean paymentProcess(OrderRequest orderRequest) {
        // 결제 처리 로직...
    }
}

위 로직에는 몇 가지 주요 포인트가 있습니다.

  • lock.tryLock(5, TimeUnit.SECONDS): 최대 5초 동안 락 획득을 시도합니다.
  • hasAlreadyOrdered(user, orderRequest.getProductId()): 사용자가 이미 상품을 주문했는지 확인합니다.
  • orderRepository.save(...): 주문 로직이 처리됩니다.
  • paymentProcess(orderRequest): 결제 로직이 처리됩니다.

마무리

스프링의 RedisLockRegistry를 활용하여 제한된 상품의 주문 처리에 대한 동시성 제어 방법에 대해 알아보았습니다. 분산 환경에서도 안정적으로 락을 관리하여 데이터 일관성을 유지하는 것이 중요하며, 이는 특히 e-commerce와 같이 동시성 제어가 필수적인 분야에서 더욱 그렇습니다.

 

반응형