Skip to content

여행 코스 설계 API 성능 최적화 및 이동 정보 캐싱 구현 완료#32

Closed
mungsil wants to merge 67 commits intodevelopfrom
feat/#31
Closed

여행 코스 설계 API 성능 최적화 및 이동 정보 캐싱 구현 완료#32
mungsil wants to merge 67 commits intodevelopfrom
feat/#31

Conversation

@mungsil
Copy link
Copy Markdown
Collaborator

@mungsil mungsil commented Jan 12, 2025

📄 PR 요약

여행 장소 간의 이동 정보를 새벽 시간대에 선캐싱하도록 구현하였습니다.

  • 여행 장소 = 축제 + 축제 주변 장소
  • 축제 및 축제의 주변 장소 위치는 변경될 확률이 낮습니다.
  • ODsay가 제공하는 이동 정보 조회 API는 실시간 교통 정보를 반영하지 않습니다.
  • 이동 정보 조회 API에는 호출 제한 횟수가 존재합니다.

이와 같은 이유로 장소 간의 이동 정보를 미리 캐싱하게 되었습니다.

캐싱된 이동 정보는 여행 코스 설계 API 에서 사용합니다. 이에 따라, 이제부터 여행 코스 설계 API는 실시간으로 이동 정보 조회 API를 호출하지 않게 됩니다.

📋 관련 이슈

🛠️ 변경 사항 설명

캐싱 로직 구현

  1. 새로운 축제 생성 → 이동정보 API request 메시지를 DB에 저장
  2. spring batch를 이용하여 request 메시지를 RabbitMQ로 전송(새벽 2시)
  3. RabbitMQ 소비자가 메시지를 받아서 Reactor 스트림으로 전달
  4. Reactor가 rate limiting 하면서 API 호출

POJO -> 엔티티

기존에 이동 정보를 나타내는 클래스는 POJO 형태였습니다. 이동 정보를 DB에 저장해야할 필요성이 생기면서, 기존 POJO 클래스를 엔티티로 변경하였습니다.

  • 클래스 이름 변경

    TransitInfo -> TransitRoute
    SubPath, BusPath, SubwayPath... -> RouteSegment, BusSegment, SubwaySegment...

  • 상속 관계 매핑
    JPA에서 제공하는 상속 관계 매핑 전략을 사용하지 않았습니다. BusSegment, SubwaySegment, WalkSegment는 RouteSegment interface를 implements 하여 다형성을 유지합니다.

  • 새로운 POJO
    출발지와 도착지 간의 총 이동 시간, 거리를 나타내는 TransitRoute 와 장소 간의 하위 이동 경로를 담고 있는 RouteSegment는 함께 사용될 확률이 높으므로 이를 위한 TransitRouteWithSegments POJO를 새로 작성했습니다.

    public class TransitRouteWithSegments {
    
    private TransitRoute transitRoute;
    private List<RouteSegment> routeSegments; }
    

도메인 이동

여행 장소를 최단 이동 시간으로 이동할 수 있는 경로를 구하는 과정을 course 도메인으로 이동시켰습니다. 초기 설계 시, 해당 과정을 course 도메인에서 수행하도록 설계하였기 때문입니다. 이에 따라 course 도메인은 최적 여행 코스를 설계하고, plan 도메인은 설계된 결과를 바탕으로 응답을 생성합니다.

📄 추가 정보

  • 성능 테스트 결과
    Grafana k6를 이용하여 캐싱 전과 후의 여행 코스 설계 API 성능을 측정한 결과, 13s -> 25ms로 응답 속도가 개선되었습니다.

  • 캐싱 데이터 관리
    캐싱된 데이터를 주기적으로 업데이트 및 삭제하는 기능이 필요합니다.

  • 캐싱 로직 성능 개선
    추후, 이동 정보 API 응답 결과를 버퍼에 저장한 이후 insert 쿼리를 날림으로써 성능 개선을 하고자 합니다.

  • 캐싱 로직 설계 변경 가능성
    현재 캐싱을 위한 로직에 배치와 메시지 큐가 사용되고 있으나, 해당 설계의 적절성에 대하여 고민해보고자 합니다.
    초기에는 Guava RateLimiter를 사용하여 호출 속도를 준수하기 위해 배치와 메시지 큐를 사용하였습니다.

    1. 배치로 메시지 전송
    2. 메시지 큐로 버퍼링
    3. RateLimiter로 속도 제어

    그러나 RateLimiter는 호출 간격을 정확하게 제어할 수 없다는 문제점을 가지고 있어 Reactor Stream을 사용하게 되었습니다.
    Reactor Stream을 사용하면 배치와 메시지 큐의 역할을 상당수 대체할 수 있으므로, 기존 설계를 변경할 가능성이 있습니다.

mungsil added 25 commits January 8, 2025 22:31
- 최단 시간 이동 경로 계산 로직을 Course 도메인으로 이동
- 장소 간 이동 경로를 실시간 API 호출 방식에서 DB 조회 방식으로 변경
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

🔍 fix 기능 수정

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant