내가 캡스톤디자인 프로젝트에서 겪은 문제 상황임
프로젝트 : 장소 목록을 등록하고 해당 장소에서 오늘 날씨 조회 기능 구현
사용자는 무조건 로그인해야 서비스를 이용할 수 있습니다.
메인 페이지 -> 날씨 보기 탭 클릭 시 날씨 페이지로 이동합니다.
해당 날씨 페이지에서는 여러개 장소 등록을 할 수 있습니다.
장소1) 대치역 - 오늘 날씨 맑음
장소2) 강남구청 - 오늘 날씨 맑음
장소3) 김포 공항 - 오늘 날씨 흐림
장소4) 부산항 - 오늘 날씨 구름 많음
이렇게 장소 등록 해두면 한번에 여러개 장소 날씨를 한 번에 볼 수 있습니다.
하지만, 현재 구조가
클라이언트 -> 서버 -> 기상청API 요청 순서로 흘러가고
기상청API에 요청하고 응답 받는데 까지 API당 3초가 걸림
list = 장소 목록 1~4번
result = []
for(장소 목록){
i번 장소로 기상청 API 요청
result.push(오늘 날씨 요청 결과)
}
으로
총 12초+@가 걸림
클라이언트 -> 서버 -> 기상청API 요청/응답 4번(3*4=12초) -> 서버 -> 클라이언트 순서
구조적 개선을 해보자 정해진 답은 없음
메인화면에서 날씨보기 눌렀는 데 로딩이 1초 이상 걸리면 사용자 이탈 가능성이 커짐
대규모 트래픽은 일단 배제하고 사용자는 얼마 없음
기상청에 api요청해서 결과받는 3초는 우리가 개선할 수 없음
기상청 API 파라미터를 더 설명하자면
DB에 사용자가 등록한 장소 저장할 때 상세 주소 -> @@동 추출해서 같이 저장해두고
@@동으로 API 요청 날림
요청 4번, 응답 4번은 미리 core개 만큼 쓰레드를 성생해두고 백그라운드로 동시에 진행하면 개선할수 있을거 같고 여기서 트래픽이 몰리면 쓰레드가 부족해지는 현상은 여러가지 방법이 있지만 간단하게 VirtualThread를 쓰기 좋은 예같아서 Virtual Thread를 사용하면 좋을거 같아
그럼 이제 근본적인 문제는 API당 3초가 걸리는 기상청 API인데 이 문제는 기상청 자체에 문제가 있는거기 때문에 근본적인 해결이 불가능임 백엔드 서버에서 최적화할 수 있는 방법중 당장 떠올르는 방법은 사용자가 가장 많이 등록된 지역 순으로 스케쥴링해서(예: 5분에 1번) 미리 캐시에 넣어두고 캐시에 넣어둔 날씨를 반환하는 거임, 등록이 적은 지역은 위에 적은 백그라운드 알고리즘을 적용해 피해를 최소화 시키는게 좋아보임
아니면 아예 모든 지역을 스케쥴링해서 디스크에 저장하거나 메모리가 충분하다면 메모리에 캐싱해 response time을 줄일수 있겠지
모든 지역을 캐싱하는게 불가능하다면 2번 댓글처럼 ㅇㅇ
만약 모든 지역을 캐싱할수 없다면 29개의 인기있는 지역이 있다해도(캐시에서 가져올수 있음) 1개의 비인기 지역때문에 전체 응답시간이 느려지는 현상이 발생할 수 있음, 이것도 캐싱된 데이터는 바로 반환하고 캐싱되지 않는 데이터는 다시 요청해서 2번의 요청으로 해결하는게 좋아보여
"역시 순수실력 3년차 주니어 급" 오늘도 완벽하게 문제해결!
1. 코어 개수만큼 스레드를 생성? 기상청api를 call하는 스레드들만 코어들을 쓴다는 가정 같은데? 이미 다른 작업들을 진행하는 스레드들이 코어를 차지하고 있을텐데? 그럼 컨텍스트 스위칭만 더 일어나는게 아닌지? 2. 문제같이 날씨가 흐림/맑음 처럼 잘 안변하는 데이터가 아니라 온도,풍량,미세먼지 처럼 계속 변하는 데이터라면?
3. 지역이 100개이상이라면 본인 말대로라면 스레드를 100개나 생성할것인지?? ㅋㅋㅋㅋㅋㅋ
1. 1번은 VirtualThread를 통해 논블록킹 I/O로 CPU time 최소화 2. 만약 날씨가 잘 변하는 데이터면 위에 작성한것과 동일하게 모든 데이터를 스케쥴링해 캐싱하는게 가장 좋아보이지만 이게 불가능하다면 가장 많이 등록된 지역 순으로 스케쥴링해서 미리 캐시에 넣어두는게 좋아보임 3. 이것도 동일함 쓰레드가 100개가 필요가 없음 VirtualThread는 논블록킹 I/O기 때문에 요청 후 새로운 작업으로 전환할 수 있음
1, 2, 3번 전부 위에 다 써져있는데 뭐가 문제인거지? VirtualThread가 뭔지 모르는거야??
1.은 조금 이상하게 썼네 VirtualThread를 통해 쓰레드 점유 시간 최소화 ㅇㅇ
무조건 vt만 쓰면 다 해결될줄 아는 학부생이로구나.. blocking io를 nonblock하게끔 vt가 해준다고 3초가 해결되겟노? 대용량 트래픽이 몰려올때 jvm단의 스케쥴링 비용은 조상님이 내주더냐
그래서 2번 글을 못읽었니? 근본적으로 3처가 걸리는 기상청 API 자체는 해결할 수 없으니까 스케쥴링을 통해 미리 캐시에 넣어둔다고 친구야 ㅋㅋ 글을 이해할줄 모르는거니?
그 캐싱하는 과정에서 쓰레드 비용 문제를 VirtualThread로 최소화 한다고
그럼 로컬 캐시의 사이즈는 어떻게 책정할거니... oom은 조상님이 막아주더냐.. gc는 조상님이 해주시더냐..
그래서 위에 적었잖아 병~신련아 메모리가 부족하면 디스크에 저장을 하면 된다고 이 시발련아 글을 좀 읽어라
자자 짖지말고.. 너의 답변에서 경험이 부족한게 드러나는구나.. 그럼 oom을 맞고 어익후 디스크에 저장해야겟놐ㅋㅋ 라는거구나...... 묻는말에 대답을 하거라..
현업에선 가용성이 최우선이거늘.. 쯧쯧
디스크에 저장하거나 메모리가 충분하다면 메모리에 캐싱해 response time을 줄일수 있겠지 << 이거 위에 안보임?
이제 하다하다 가용성으로 ㅈㄹ하네 SPOF가 문제라고? ㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ 진짜 미치겠다
이 답변 ㄹㅇ 면접장에 들어온 우매봉에 잇는 학부생같아서 기엽노
레디스를 사용해 TTL을 줘서 스케쥴링 N분 단위로 줘서 최적화할수도 있고 방법은 많아
레디스 자체는 인기있는 지역만을 관리하고 DB에서는 비인기 지역까지 스케쥴링해서 관리해도 되고 TTL 자체를 짧게줘서 메모리 관리를(이미 과거 날씨는 의미가 없으니까) 최적화 시킬수 있고
바로 위에 대댓 두개는 너가 전혀 질문을 이해하지 못하고 있어 더 성장해라 강아지 그럼 이만
DB와 레디스를 적절하게 사용하면 OOM이나 이런 문제는 발생안해 그리고 가용성 문제는 친구야 ㅋㅋ 이제 깔게없어서 가용성 ㅇㅈㄹ 하는게 참 귀엽다
이거 위에 안보임? << 요기부터 두개 ㅇㅇ
그럼 우리 ㅈ고수이신 현업자분께서는 이 문제에 대해 어떤 솔루션이 있으시나요?
설마 솔루션 제시는 못하고 문제만 지적하는 허수 개발자 아니시죠?
혹시해서 물어보는건데 vt 아니어도 nio 쓸수있는건 알고말하는거지? 왜 vt 얘기를 먼저꺼낸건지 이해가안되서
쓰레드가 부족해지는 현상의 관점에서 VT를 말한거임 결국 쓰레드를 최대한 효율적으로 사용하려면 VT가 필요함 NIO랑 애초에 해결하려는 문제가 다른데
쓰레드가 부족할수있다는게 inbound 관점을 말하는거야 outbound 관점을 말하는거야 ㅋㅋ 둘다 사용법은 결국 nio로 해결할수있는 문제고 vt가 아니어도 되는문제인데 왜 vt를 끌고오냐는게 질문임. 바꿔말하면, vt가 없던시절에는 이문제를 어떻게해결하겠냐는거지
내가 문제를 좀 잘못이해했음 VT가 굳이 필요없음 NIO로 해결가능함
날씨는 실시간 변경이 안중요하니 서버에 옮겨놓고 쓰는게 낫지
지금 이미 서버에서 기상청에 요청에서 결과받아오는 구조임 - dc App
그니까 기상청api요청과 클라이언트 날씨요청을 분리해야된단거임 서버 기상청 요청 -> 서버 저장 클라이언트 날씨 요청 -> 저장된걸 서버에서 응답
ㅇㅎ 이해함ㅇㅇ - dc App
최초 비동기 병렬처리 요청 + 지역별 데이터 캐싱 정도? 근데 장소등록을 여러개 가능하면 이론상 한 사용자가 무한대의 장소를 등록할 수 있는거임?
무제한은 악의적인 용도로 가능하니 대충 넉넉히 30개정도로 제한 - dc App
기상청 날씨데이터 리프레시 시점을 예측해서 스케줄링으로 요청하고 메모리나 디비에 넣고 쓰는게 가장 경험이 좋긴한데 캐시랑 db백업 정도 외에는 모르겠네 인스턴스가 두대라면 하나는 주기적으로 요청해서 최초에는 디비에 넣고 이후에 디비에 존재하는 요소는 업데이트 여부만 판별해서 메세지큐로 전달하고 다른 인스턴스에서 받아서 메모리에 업데이트 해둔다던지
기상청 API 찌를 때 파라미터가 다 다름? 캐싱하기 힘든 구조임?
아 이걸 말안했네 @@동 단위로 파라미터 보냄 - dc App
@@동이 어딘지는 사전에 연산해서 저장해둬 - dc App
장소 등록할 때 상세 주소 -> @@동 추출해서 같이 저장 후 @@동으로 api요청 - dc App
동단위면 그냥 서버에서 캐싱하고, 캐싱된 애들을 별도 worker에서 주기적으로 갱신해주면 될 거 같은데?
걍 시간단위로 배치돌려서 모든지역 다 받아놓고 보여주면 안되나
하루동안 날씨가 뭐 엄청 급격하게 변할것같지도 않고
가능한데 3600개라 좀 많긴하네 - dc App
사용자가 등록해놓은걸로 스케줄러 돌려서 캐싱해야지. 아니면 걍 api 바꿔
보통 데이터가 단위 시간으로 업데이트 된다고 가정하면 1. 마지막 업데이트 단위와 현재 업데이트 단위가 같으면 API 요청 X. 2. 1이 아닌 경우 (API 호출이 필요한 경우) 메인화면에서 날씨 탭 들어가기 전 API 요청 (API 요청은 병렬 처리)
그리고 저 위 사진처럼 모든 동이 등록되어 있다고 가정하고 rate limit이 있다고 할 때 현재 위치에서 가까운 순으로 우선순위 두고 먼저 API 요청? 이건 그냥 떠올라서 한 번 달음