조급하면 모래성이 될뿐

10명이 동시에 2GB의 파일을 업로드 한다면 ? (Multipart) 본문

TroubleShooting/Etc

10명이 동시에 2GB의 파일을 업로드 한다면 ? (Multipart)

Pawer0223 2023. 10. 17. 22:32

결론

- 동시에 처리 가능한 스레드 풀 개수를 조절하여 리소스(CPU) 사용률을 관리하자

- 큰 파일 업로드 시 메인 메모리에 대한 걱정은 거의 없다. 임시 디스크에 대한 경로를 직접 설정해서 혹시 남아있는 경우가 있다면 직접 관리하자.

- 네트워크 대역폭을 넓히거나 하드웨어성능을 높여보자. (가능하다면)

 

업로드 로직

application.yml

spring.servlet.multipart.max-request-size: 3GB

spring.servlet.multipart.max-filet-size: 3GB

 

UploadService

    fun upload(multipartFile: MultipartFile) {
        val elapsedTime = measureTimeMillis {
            if (multipartFile.isEmpty) {
                throw IllegalArgumentException("파일 없음")
            }
            val originalFileName = multipartFile.originalFilename ?: throw IllegalArgumentException("파일 이름 이상함")
            val newFileName = createFileName(originalFileName)
            multipartFile.transferTo(File(getFullPath(newFileName)))
        }
        log.info("${Thread.currentThread().name} exec $elapsedTime ms")
    }

 

테스트

1.8GB의 파일을 동시에 10개를 업로드한다.

JMeter를 통해 호출했고, 그라파나를 통해 모니터링했다.

수행 속도는 평균적으로 30~ 40초의 시간이 걸렸다.

모니터링 결과를 통해 파일 업로드 과정을 이해할 수 있다.

 

1. 메인 메모리를 많이 사용하지 않는다.

처음엔.. 업로드를 위해서 최소 18GB의 메모리 공간이 필요하다고 생각했다.

그러나 모니터링 결과 JVM메모리는 거의 사용하지 않았다.

1.8GB의 바이트 코드가.. 각각 요청이 올 텐데.... 왜 Heap메모리를 사용하지 않는 걸까.....?

 

이유는 Mulitpart 사용해 업로드 시 스트리밍 방식으로 디스크에 저장하기 때문에 메모리 사용을 최소화하고 있기 때문이다.

- MultipartFile.transferTo

 

spring.servlet.multipart.location 설정으로 디스크 저장 시 사용될 임시디렉터리 지정

spring.servlet.multipart.file-size-threshold 설정 값 이하라면 디스크가 아닌 메모리를 사용한다.

 

 

업로드 호출 전
업로드 10개 호출 후
업로드 파일

 

2. CPU 사용률은 늘어난다.

당연한 얘기지만, 동시에 10개의 스레드에서 열심히 File I/O가 수행되기 때문이다.

10개의 요청에는 짧은 시간 100%를 찍지만 요청이 늘어날수록 응답속도는 느려질 것이다.

 

스레드 개수 조절해 보기

위 결과를 통해 여러 스레드가 동시에 업로드될수록 느려질 수 있다는 것을 알았다.

그럼 동시 처리가능한 개수를 조절한다면 속도를 개선할 수 있을까???

 

10개의 파일을 업로드할 때, 2개의 스레드를 사용해 처리할 때 10개의 스레드를 사용할 때 어떤 것이 더 빠를까??

server:
  tomcat:
    threads:
      min-spare: 5 // 활성 스레드 개수
      max: 5 // 생성 가능한 스레드 최대 개수
    accept-count: 100 // 작업 큐 사이즈

 

10개 스레드: 29초

2개의 스레드: 9초

이렇게 5번이 돈다, 따라서 총 수행시간은 9 * 5는 45초 정도 걸렸다.

5개 스레드: 14초

14초씩 2사이클을 돌기 때문에 총 수행시간은 28초 정도 걸렸다.

 

스레드 개수에 따른 CPU 사용률

- 초록 그래프: 시스템 사용량

- 노란 그래프: 프로세스 사용량

10개, 5개를 사용했을 때 전체 처리시간은 비슷했다. (약 30초) 하지만 10개 스레드의 경우 시스템 CPU 사용률이 98.98% 되어 더 이상 다른 요청을 처리할 수 없는 수준이 된다.

반면 5개 스레드로 처리할 때는 80% 정도기 때문에 10개와 비교하여 20% 정도의 여유를 가질 수 있다.

(스레드 개수에 따른 CPU사용률은 각 컴퓨터 사양마다 다르다.)

 

2GB 파일 업로드 30개 요청을 스레드 개수가 5개, 10개 일 때 비교

이것도 처리속도는 비슷하다. 스래드 개수가 10개일 때는 30초씩 3번 돌았고, 5개일 때는 16초씩 6번 돌았다.

CPU 사용률 또한 기존과 유사하다.

다만 10개 스레드로 처리하는 경우엔 예외 케이스가 존재했다. 서버 리소스가 충분하지 않은대 10개의 요청을 처리하면서 발생하는 문제로 보인다. (정확하지 않음...)

 

정리

아무래도 각 파일을 네트워크 전송하는 과정이 포함되기 때문에, 스레드 개수만으로 전체적인 수행속도시간을 획기적으로 줄이기는 쉽지않을 것 같다. 다만 스레드 개수에 따라 CPU사용률에 따른 병목구간을 확인하고, 자원을 효율적으로 활용할 수 있다.

 

좀 더 효율적으로 바꿀 수 있을까??

- 네트워크 대역폭을 늘려 한번에 전송 가능한 양을 늘릴 수 있다.

- 스케일 업을 통해 서버 리소스를 증설한다.

- 코드 레벨에서는 여러 가지 방식이 있다.

- AWS 활용 시 업로드 방법 비교 - [ 참고- 우아한 기술 블로그 ]

- TUS 사용 - [ 참고 ]

반응형

'TroubleShooting > Etc' 카테고리의 다른 글

동시성 처리하기 - 낙관적 락 + 재시도  (1) 2023.09.25