-
Notifications
You must be signed in to change notification settings - Fork 37
[3주차] 예약 API 구현 및 분산락 적용 #83
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 1 commit
e407e9c
d34a68b
82ce364
b6c91f5
2e43f50
7afcc1d
df23690
c441a56
56b1505
78ec165
2e50fc0
ad7edce
54be6e6
660f36b
4f7ef63
51f364c
90f9eec
7991a01
3d95f66
b1d4dfe
9556ab8
8f803bb
8019bcd
b36f91c
7b3029f
db53d5e
106e92b
ee48288
93c14b7
5351541
b9672af
5a29de5
b77c6fb
3382fb3
cb2d0f2
66b4601
f82330d
37b606f
f35814a
e4e03cf
be09082
06aff06
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,7 @@ | ||
| package com.example.application.lock; | ||
|
|
||
| import java.util.function.Supplier; | ||
|
|
||
| public interface DistributedLockExecutor { | ||
| <T> T executeWithLock(String key, long waitTime, long leaseTime, Supplier<T> task); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,39 @@ | ||
| package com.example.application.lock; | ||
|
|
||
| import java.util.concurrent.TimeUnit; | ||
| import java.util.function.Supplier; | ||
| import org.redisson.api.RLock; | ||
| import org.redisson.api.RedissonClient; | ||
| import org.springframework.stereotype.Component; | ||
|
|
||
| @Component | ||
| public class RedissonDistributedLockExecutor implements DistributedLockExecutor { | ||
|
|
||
| private final RedissonClient redissonClient; | ||
|
|
||
| public RedissonDistributedLockExecutor(RedissonClient redissonClient) { | ||
| this.redissonClient = redissonClient; | ||
| } | ||
|
|
||
| @Override | ||
| public <T> T executeWithLock(String key, long waitTime, long leaseTime, Supplier<T> task) { | ||
| RLock lock = redissonClient.getLock(key); | ||
| boolean acquired = false; | ||
|
|
||
| try { | ||
| acquired = lock.tryLock(waitTime, leaseTime, TimeUnit.MILLISECONDS); | ||
| if (!acquired) { | ||
| throw new IllegalStateException("[ERROR] Unable to acquire lock for key: " + key); | ||
| } | ||
| return task.get(); | ||
| } catch (InterruptedException e) { | ||
| Thread.currentThread().interrupt(); | ||
| throw new RuntimeException("[ERROR] Lock acquisition interrupted", e); | ||
| } finally { | ||
| if (acquired) { | ||
| lock.unlock(); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -2,7 +2,7 @@ | |
|
|
||
| import com.example.application.dto.request.ReservationRequestDto; | ||
| import com.example.application.dto.response.ReservationResponseDto; | ||
| import com.example.application.lock.DistributedLock; | ||
| import com.example.application.lock.DistributedLockExecutor; | ||
| import com.example.application.port.in.MessageServicePort; | ||
| import com.example.application.port.in.ReservationServicePort; | ||
| import com.example.application.port.out.MemberRepositoryPort; | ||
|
|
@@ -20,11 +20,15 @@ | |
| import com.example.domain.model.entity.ScreeningSeat; | ||
| import java.util.ArrayList; | ||
| import java.util.List; | ||
| import java.util.function.Supplier; | ||
| import java.util.stream.Collectors; | ||
| import lombok.RequiredArgsConstructor; | ||
| import lombok.extern.slf4j.Slf4j; | ||
| import org.hibernate.exception.LockAcquisitionException; | ||
| import org.springframework.stereotype.Service; | ||
| import org.springframework.transaction.annotation.Transactional; | ||
|
|
||
| @Slf4j | ||
| @RequiredArgsConstructor | ||
| @Service | ||
| public class ReservationService implements ReservationServicePort { | ||
|
|
@@ -38,8 +42,8 @@ public class ReservationService implements ReservationServicePort { | |
| private final ReservationValidationPort reservationValidationPort; | ||
|
|
||
| private final MessageServicePort messageServicePort; | ||
| private final DistributedLockExecutor distributedLockExecutor; | ||
|
|
||
| @DistributedLock(key = "'seat_reservation:' + #request.screeningId + ':' + #request.seatIds.toString()") | ||
| @Transactional | ||
| @Override | ||
| public ReservationResponseDto create(ReservationRequestDto request) { | ||
|
|
@@ -48,10 +52,21 @@ public ReservationResponseDto create(ReservationRequestDto request) { | |
|
|
||
| List<ScreeningSeat> requestedSeats = validateReservationConstraints(screening, member, request.seatIds()); | ||
|
|
||
| Reservation reservation = saveReservationAndSeats(screening, member, requestedSeats); | ||
| sendReservationConfirmation(member, requestedSeats, screening); | ||
|
|
||
| return ReservationResponseDto.fromEntity(reservation); | ||
| // 함수형 분산 락을 특정 메서드에 적용 | ||
| String lockKey = "seat_reservation:" + request.screeningId() + ":" + request.seatIds(); | ||
| long waitTime = 2000; | ||
| long leaseTime = 1000; | ||
|
||
|
|
||
| try { | ||
| return distributedLockExecutor.executeWithLock(lockKey, waitTime, leaseTime, | ||
| () -> { | ||
| Reservation reservation = saveReservationAndSeats(screening, member, requestedSeats); | ||
| return ReservationResponseDto.fromEntity(reservation); | ||
| }); | ||
| } catch (LockAcquisitionException e) { | ||
| log.warn("[락 실패] 좌석 예약에 대한 락을 획득하지 못함 - Key: {}", lockKey); | ||
| throw new CustomException(ErrorCode.LOCK_ACQUISITION_FAILED, "좌석 예약 요청이 많아 처리가 지연되었습니다. 다시 시도해주세요."); | ||
| } | ||
| } | ||
|
|
||
| /** 상영 정보 조회 */ | ||
|
|
@@ -112,4 +127,5 @@ private void sendReservationConfirmation(Member member, List<ScreeningSeat> requ | |
| requestedSeats.stream().map(ss -> ss.getSeat().getSeatNumber().toString()).collect(Collectors.joining(", ")), | ||
| screening.getMovie().getTitle(), screening.getTheater().getName(), screening.getStartTime())); | ||
| } | ||
|
|
||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
적절하게 잘 구현해주셨습니다 👍