KTX의 빈자리 알림을 보내주는 서비스를 개발하고 있습니다.
개요
사용자가 즐겨찾기 한 열차 티켓의 여석이 생겼을 때, 알림을 보내는 기능은 저희 서비스의 핵심 기능입니다.
또한 성능과 안정성이 굉장히 중요합니다. 알림 속도가 몇 초만 늦어도 다른 사용자에게 좌석을 빼앗길 수 있기 때문입니다.
그래서 성능을 최대한 좋게 하기 위한 방법들을 생각해 냈습니다.
기능 동작 방식
여석 알림을 보내주는 기능은 다음과 같은 순서로 이루어 집니다.
1. 현재 시각 이후, 아직 유저에게 알림을 보내지 않은 모든 즐겨찾기 티켓을 조회합니다.
2. 같은 티켓을 여러명이 즐겨찾기 한 경우, 동시에 사용자들에게 알림을 보내야 공정하다고 생각하여 티켓과 해당 티켓을 즐겨찾기한 사용자 목록을 매핑하는 데이터 구조인 Map<Ticket, List<BookMark>>을 만듭니다.
private Map<Ticket, List<BookMark>> makeBookMarkUserMap(List<BookMark> bookMarkList) {
Map<Ticket, List<BookMark>> ticketBookMarkMap = new HashMap<>();
for (BookMark bookMark : bookMarkList) {
if (ticketBookMarkMap.containsKey(bookMark.getTicket())) {
ticketBookMarkMap.get(bookMark.getTicket()).add(bookMark);
} else {
List<BookMark> list = new ArrayList<>();
list.add(bookMark);
ticketBookMarkMap.put(bookMark.getTicket(), list);
}
}
return ticketBookMarkMap;
}
3. 크롤링을 통해 사용자들이 즐겨찾기 한 열차 데이터의 여석을 코레일에서 확인합니다.
4. 받은 요청을 분석하여 여석이 생긴 경우, AWS SNS 를 이용해 사용자에게 문자를 보냅니다.
1. 커넥션 풀 이용
기존 코드에서는 크롤링 작업에서 매번 HttpClient 객체를 생성하고, 커넥션을 새롭게 생성해야 했습니다.
@Bean
public CloseableHttpClient httpClient() {
return HttpClients.custom()
.setConnectionManager(poolingHttpClientConnectionManager())
.setKeepAliveStrategy(getKeepAliveStrategy())
.build();
}
private PoolingHttpClientConnectionManager poolingHttpClientConnectionManager() {
PoolingHttpClientConnectionManager connManager = new PoolingHttpClientConnectionManager();
connManager.setMaxTotal(100);
connManager.setDefaultMaxPerRoute(20);
return connManager;
}
private ConnectionKeepAliveStrategy getKeepAliveStrategy() {
ConnectionKeepAliveStrategy keepAliveStrategy = (response, context) -> {
BasicHeaderElementIterator it = new BasicHeaderElementIterator(
response.headerIterator("Keep-Alive"));
while (it.hasNext()) {
HeaderElement he = it.next();
String param = he.getName();
String value = he.getValue();
if (value != null && param.equalsIgnoreCase("timeout")) {
return TimeValue.ofSeconds(Long.parseLong(value));
}
}
return TimeValue.ofSeconds(1200); // 기본 Keep-Alive 시간 설정
};
return keepAliveStrategy;
}
그래서 커넥션 풀을 만들고 빈으로 등록 했습니다.
그 결과 불필요한 커넥션 생성이 줄어들어 즐겨찾기 100개를 크롤링 하고 문자를 보내는 기준 2분 30초 정도 걸리던 작업이 1분 정도로 줄어들었습니다.
2. AWS SNS 비동기 처리
분석
AWS SNS로 문자를 보내는 로직은 즐겨찾기 한 건당 0.3초 ~ 0.5초가 소요됩니다.
여석이 생겼음을 확인한 후 SNS 문자를 보내는 작업은 응답을 기다릴 필요가 없다고 판단하여, 성능 향상을 위해 비동기 방식으로 개선하기로 결정했습니다.
성능 개선
@Async
@Transactional
public CompletableFuture<Void> sendSnsForBookMark(String message, String phoneNumber, BookMark bookMark) {
long startTime = System.currentTimeMillis();
try {
PublishRequest smsMessage = PublishRequest.builder()
.message(message)
.phoneNumber(phoneNumber)
.build();
PublishResponse publish = snsClient.publish(smsMessage);
bookMark.messageIsSent();
bookMarkRepository.flush();
log.info("messageId: {} , body: {} ", publish.messageId(), publish);
} catch (Exception e) {
log.error("문자 전송 실패: {}", e.getMessage());
throw new RuntimeException(e);
}
long estimatedTime = System.currentTimeMillis() - startTime;
System.out.println("문자 보내는데 걸린 시간 : " + estimatedTime / 1000.0 + " seconds");
return CompletableFuture.completedFuture(null);
}
다음과 같이 @Async 어노테이션을 사용하여 AWS SNS를 호출하고, 사용자에게 알림을 보내는 로직을 비동기 방식으로 처리했습니다.
이를 통해 크롤링 도중 AWS SNS 호출로 인한 시간 지연을 방지할 수 있게 되었습니다.
결과
77.113 Sec ➡️ 48,726 sec로 성능 개선 되었습니다!!
'기술적 고민 > 자리나따' 카테고리의 다른 글
[자리나따] 대량의 데이터를 빠르게 DB에 넣기 위한 고민 (0) | 2024.11.24 |
---|---|
[자리나따] flyway 적용기 (Spring boot, JPA, MySQL) (1) | 2024.11.22 |
[자리나따] 웹 크롤러를 개발 하며 하는 고민 (2) - 데이터 흐름 (0) | 2024.07.22 |