동적 병렬 스레드 다운로드

python을 통해 치지직 vod 다운로드 프로그램을 제작 중이다.

치지직 vod 컨텐츠는 네이버 api에서 제공되며 누구나 요청을 통해 동영상을 다운로드 받을 수 있다.

현재 구현중인 기능은 static한 병렬 스레드 다운로드 기능을 dynamic하게 변경하는 기능이다.

스레드 다운로드를 구현한 이유는 api 한 번의 요청으로 네이버 서버에서 내려받는 속도가 1~2MB/s정도로 느리기 때문이다. 한 번에 여러 api 요청을 보낸다면 이론적으로 클라이언트 인터넷 속도만큼 다운로드 받을 수 있을 것이라고 생각했고, 이는 다운로드의 효율성을 극대화 시킬 것이다.

병렬 다운로드를 하기 위해서는 메타데이터에 기록된 동영상 크기에 대해서 20MB씩 조각내여 다운로드 할것이다.

나는 ThreadPoolExecutor 를 사용하여 스레드 풀에서 필요한 만큼 스레드를 꺼내와 병렬적으로 다운로드를 시킬 예정이다.

스레드 풀의 max_workers 도 얼마나 만들어야 할 지 생각 중이다. 일단 생각해본 바로는 조각의 갯수만큼 필요하지 않을까 싶다. 또한 max_workers 에서 활성화시킬 adjust_threads 들도 얼마나 만들어야 하는지 고민 중이다. optimal한 다운로드 스레드 수를 구하기는 어렵기 때문에 최적의 스레드 증가 함수를 연구해야 할 것이다.

내 의도는 max_workers 들 중에서 adjust_threads 만큼의 스레드를 가져와서 그 친구들로 하여금 작업을 반복시킬 계획이었으나, 현재는 작업이 완료된 스레드들은 그대로 종료되고 풀에서 다음 스레드들을 가져오는 방식으로 진행되고 있다. 어느 방식이 더 좋은지는 연구해봐야 한다.

또한 사용자 네트워크 대역폭을 고려하여 adjust_threads 를 linear하게 늘리는 것이 아닌 exponential하게 늘려야 할 것이다. 이 뿐만이 아니라 얼마나 주기적으로 늘릴 것인지 효율적인 timing도 구해야 할 것이다.

원래 의도대로 스레드 풀에서 adjust_threads 만큼 스레드를 가져와서 작업하도록 수정되었다. 다음으로 고민해봐야할 문제는 target을 얼마나 조각내야 할지 정해야 한다. 우선적으로 20MB 크기의 조각을 내어서 하나의 스레드가 다운로드 받기로 정했다. 그러나 target의 용량이 매우 적은 경우에는 원하는 만큼의 조각을 내기가 힘들다. 그러므로 용량에 1/n씩 조각 내기로 했다. 그러면 생각해볼 것은 n의 값은 얼마나 정해야 하는가 이다. n 값이 적으면 조각이 많이 나지 않을 것이고, n값이 크다면 스레드 작업 간 시간 소모가 매우 크게 될 것이다. 적절한 n값을 구해야 할 것이다.

나중에 생각해 봤으나, 굳이 n/1이 아닌 화질 별로 크기를 나누는게 더 합리적일 것이라고 생각했다. 현재 치지직 동영상 화질은 144p, 720p, 1080p 3가지로 구성된다. 144p는 1MB, 720p는 5MB, 1080p는 10MB로 나누니 적당한 세그먼트 개수를 가지게 된다.

초기 스레드 갯수는 그렇게 중요하지 않다고 본다. 왜냐하면 다운로드 서비스 자체가 I/O bound 하기 때문에 네트워크 대역폭에 가장 큰 영향을 받기 때문이다. 나는 32와 cpu 스레드 수 + 4 의 최솟값을 초기 스레드로 저장했다. 그나마 시스템의 성능을 고려한 대처이다.

유난히 다운로드 속도가 느린 스레드가 존재한다. 이 스레드들은 다른 스레드들에게 네트워크 대역폭 점유를 빼앗긴 경우에 해당하는것 같다. 특히 무선 네트워크 환경에서 이런 현상은 자주 일어난다. 느린 스레드는 다운로드 속도를 회복하지 못하기 때문에 약 30초가 지나면 API 통신이 끊긴다. 가장 극단적인 상황은 마지막으로 남은 스레드가 느린 스레드인 경우, 스스로 통신이 끊길 때 까지 대기해야 한다.

이를 방지하기 위해서는 스레드가 느리다는 것을 감지하고 스스로 종료하도록 해야한다. 다운로드 속도가 100KB/s이하인 경우가 5초 이상 지속된다면 자원을 반납하고 다음 스레드에게 해당 구간을 넘겨 주어야 한다. 다운로드 속도가 느리기에 손실되는 다운로드 양과 속도는 전체 다운로드 양과 속도에 큰 영향을 끼지지 못한다.