업데이트 프로젝트 진행 과정 정리(백엔드) #1

햐얀하늘·2024년 7월 4일
0

1. 프로젝트 간략 소개

현재 진행하고 있는 프로젝트는 외부 서버에서 파일을 받아와서 클라이언트에게 파일을 다운로드가 가능하도록 하는 프로젝트를 수행하고 있다. 이 프로젝트의 메인 기능은 4가지로 볼 수 있다.

  • 파일을 받아와서 전달
  • 모니터링 시스템(헬스 체크)
  • 라이선스 생성 및 클라이언트에게 전송
  • ssl 인증서 발급, 재발급 및 전송

4가지 기능으로 볼 수 있다.

2. 고민들

1. 파일을 어떻게 전달할 것인가??

  • 파일의 특징 :
    • 저용량~ 대용량까지 범위가 크다(수십메가에서 1GB까지) => 대용량 파일 전송은 어떻게 할까?
    • 한번에 많은 요청이 몰릴 수 있다. => 부하처리를 어떻게 할까?
    • 클라이언트마다 필요한 파일의 버전이 다를 수 있다. => 클라이언트에게 맞는 데이터는 어떻게 줘야할까?
    • 데이터를 어떻게 저장하고 관리할 것인가 => 어떤 db를 활용해야 효율적인 관리가 가능할까?
    • 전송 속도 및 실패 시 대응책 => 퍼포먼스 및 에러 발생 시 대응법

2. 고려사항에 대한 고민

1. 파일 전송에 관해서

  • 파일 전송 :
    • FTP서버 이용 : 현재 FTP 서버를 새로 구축할 만큼 매번 1GB만큼 큰 데이터 파일을 주는것이 아니다. 또한 ftp방식으로 데이터를 보낼 시 장비에서 악성으로 판단해 허용하지 않는다. 예외처리를 해줘야한다.
    • HTTP, stream 이용 : 가벼운 데이터는 http로 파일 전체를 받아오고 대용량 파일의 경우에는 스트림으로 chunk방식으로 데이터를 받아올 수 있다. 만약 전송이 끊어질 시에 대처하는 코드를 추가적으로 넣어 대용량파일에 적합하다.
    • 메시지 큐잉 시스템 이용 : 대용량 파일 전송에는 비효율적이다.
    • nginx 이용 파일 다운로드 : 파일이 단순히 하나가 아니고 다수 접속시 부하가 예상된다. nginx보다 백엔드 서버나 다른 프로토콜 쓰는 것이 좋아보임.
    • 병렬 다운로드 : 다운로드 속도가 빠르고 분할해서 다운받기 때문에 실패 chunk에 대해서만 재시작 가능하여 고려할 순 있으나 한번에 많은 요청이 왔을 때 서버에 부하가 걸려 퍼포먼스의 질이 떨어질 것 같다.
    • CDN : 폐쇄망이기 때문에 불가능하다.
    • redis와 같은 캐시 이용 : 500MB가 넘는 대용량 파일을 redis cache에 넣는것은 어렵다. redis가 유료로 전환될 가능성이 있다.

이러한 고민을 통해 http, stream을 활용해서 파일을 전송하고자 생각하고있다.

2. 서버 부하에 관해서

  • 서버 부하 :
    • 한번에 많은 요청시 어떻게 처리 할 것인가??
    1. 로드밸런싱 => 서버를 여러개 띄워서 서버의 부하에 따라 다른 서버에서 동작 수행
    2. 네트워크 대역폭 조절 => 많은 요청 시 네트워크 대역폭을 줄여서 파일 다운로드 수행
    3. 유저 분산 처리 => 많은 유저가 들어왔을 때 다운로드 실패한 유저들에게는 추가 시간을 준다. 그래서 일정 시간이 지나면 다시 다운로드를 재시작하도록 진행
    4. HTTP/2 프로토콜 이용 => 멀티플렉싱, 헤더 압축을 통해 네트워크 대역폭을 효율적으로 사용할 수 있다.
    5. 단순히 서버 성능 확장
    6. cluster와 worker thread
  • 로드벨런싱 : 도커를 활용하고자 한다. 도커 이미지로 컨테이너에서 nestjs 실행시킨 뒤 nginx로 서버의 부하에 따라 로드벨런싱 진행, 단 cpu 코어, 메모리 등 리소스에 대한 고려가 필요하다.
  • 네트워크 대역폭 조절 : 100의 네트워크를 가지고 있다고 가정하자. 만약 5명의 사람이 요청을 하면 각각 20의 네트워크 대역폭을 할당시킨다. 근데 20명의 사람이 요청하면 각 5의 대역폭을 할당해서 부하를 낮춘다. 단 네트워크 대역폭이 작을 수록 속도가 느려지는 문제를 고려해야한다.
  • 유저 분산 처리 : 에러발생 시의 후처리라고 보면된다. 동시에 부하를 감당할 수 없어 결국 따로 개인 다운로드 시간을 주는 것인데 이러면 동시성이 깨지기 때문에 주된 해결책이라고 보긴 어렵다.
  • http/2 프로토콜 : 클라이언트 서버에서 http1.1을 쓰고 있어서 http2 쓰는 것은 무의미하다고 판단. 또한 http2 프로토콜의 취약점이 또 발생했어서 클라이언트 서버 http2 프로토콜로 업데이트하기에는 무리가 있어보임. 또한 메인 해결책이라고 보긴 어려움
  • 서버 성능 향상 및 확장 : 가장 단순하면서도 직관적인 방법. 돈 문제 무리가 있어보임
  • cluster와 worker thread: 로드벨런싱과 비슷하지만 nest 서버를 도커로 띄우는것이 아니라 nest 특성상 단일 스레드라 멀티 코어를 효율적으로 사용하지 못한다. 그래서 cluster와 worker thread로 따로 멀티코어를 효율적으로 활용하는 것을 생각해 보았다. 이것의 장단점은 좀 더 알아봐야하고 어느정도까지 커버가 가능한지 확인해봐야한다.

그래서 어떻게 할 것 인가??
로드벨런싱과, 네트워크 대역폭 조절, 유저 분산 처리 3가지를 사용해보고자 한다.
로드벨런싱으로 1차로 서버의 부하정도를 줄이고 그 부하를 다 감당할 수 없을 정도로 많아지면 네트워크 대역폭을 줄여서 2차로 분산 시킨다. 만약 네트워크 대역폭을 더 줄이면 속도나 성능이 너무 떨어져 사용할 수 없다고 판단된다면 유저 분산 처리를 통해 서버 부하를 줄이고자 한다.

즉 로드밸런싱 -> 네트워크 대역폭 조절 -> 유저 분산처리 순이다.

3. DB에 관해서

  • NoSQL(MongoDB) : 데이터를 받을 때 json으로 받기 때문에 해당 데이터를 굳이 분리해서 관계형 데이터베이스에 집어넣는 것보다 MongoDB로 nested 형식으로 버전별로 관리하는 것이 더 조회하는것에서 더 성능이 좋아 MongoDB를 선택하고자 했다.
  • RDBMS(MariaDB, sqlite3) : 데이터들의 관계가 서로 연관된 정보가 거의 없기 때문에 굳이 관계형 DB를 쓸 필요가 없다. 또한 db에 넣을때도 json 데이터를 다 분리해야하기 때문에 비효율적이라 판단했다.

4. 클라이언트 데이터 저장 방식에 대해서

  • 현재 데이터를 저장하는 방식이 객체로 온 json 데이터를 하나하나 sql query문을 만들어서 db에 저장한다. 이때 insert or replace를 이용해서 데이터를 처리하게 되는데 데이터의 형식이 모두 동일하기 때문에 매번 수십수백번의 insert 쿼리를 만들어서 데이터를 저장하는 것보다 bulk insert로 데이터를 한번에 집어넣는것이 더 좋을 것이라 판단.

  • insert 쿼리를 매번 만들어서 저장하는 것이 아니라 bulk insert문을 만들어서 저장하는 방식으로 진행하고자함

profile
나는 커서 개발자가 될거야!

0개의 댓글