ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 스프링의 RedisLockRegistry를 활용한 제한된 상품의 동시성 제어 및 주문 처리
    Tech/SpringBoot 2023. 10. 9. 15:15
    반응형

    개요

    이 포스팅에서는 스프링의 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와 같이 동시성 제어가 필수적인 분야에서 더욱 그렇습니다.

     

    반응형
Designed by Tistory.