Fireball 프로젝트는 대용량 파일을 빠르게 전송할 수 있는 작은 독립 서비스를 제공합니다. 어떤 언어, 어떤 프레임워크를 사용하든지 간단한 HTTP 요청을 통해 원격지와 파일을 빠르게 교환할 수 있습니다.
Method: POST
URL: http://localhost:58080/api/upload
Content-Type: application/json
{
"source": {
"file": "source.dat"
},
"destination": {
"ip": "127.0.0.1",
"port": 50711,
"file": "destination.dat"
}
}
누구나 네트워크를 통해 파일을 교환하는 코드를 어렵지 않게 작성할 수 있습니다. 그러나 어떤 도구를 사용하고, 도구를 얼마나 최적화하느냐에 따라 처리 성능에 큰 차이를 보입니다.
저는 몇몇 대표적인 방법(Fireball 포함)을 사용해 1GB 크기의 파일을 루프백 네트워크로 전송하는 테스트를 진행했습니다. 50회 반복 테스트 수행 후 평균치를 측정했고 테스트에는 일반적인 성능의 홈 노트북(Acer Swift 5 Notebook)을 사용했습니다. 1회 측정 시간은 파일을 읽고, 네트워크로 전송하며, 수신된 파일을 다시 저장한 후, 응답이 완료되기까지의 시간을 포함합니다. 아래의 테스트 결과를 보면 동일한 조건에서 동일한 파일을 처리하지만 어떤 방법을 사용하느냐에 따라 큰 성능 차이를 가져온다는 것을 알 수 있습니다.
위 테스트 결과를 보면 최고의 성능을 보여주는 방법이 Fireball은 아닙니다. 위 방법들 중에서는 윈도우 FTP 서버를 띄우고 .NET API를 사용했을 때 최고의 성능을 보여줍니다. Fireball 프로젝트는 앞으로 더 나은 성능을 지속적으로 추구할 것입니다. 또한 다음과 같은 핵심가치를 함께 제공하고자 합니다.
Fireball은 Spring Boot 프로젝트이며 빌드툴로 Maven을 사용합니다. 이제 프로젝트를 clone하고 루트 경로(mvnw 파일이 존재하는 경로)로 이동한 다음 mvnw(maven wrapper)를 사용해 프로젝트를 빌드할 수 있습니다. 빌드 결과 파일은 ./target/fireball-0.0.1.final.jar 에 존재합니다.
> git clone https://github.com/joosing/fireball.git
> cd fireball
> chmod 744 mvnw
> ./mvnw clean package -DskipTests
Fireball을 사용해 서로 다른 네트워크 호스트 간 파일을 교환하려면 양 쪽 호스트 모두에 Fireball 서비스가 설치되고 실행되어야 합니다.
> java --version
> cp ./target/fireball-0.0.1.final.jar /app/fireball/fireball-0.0.1.final.jar
다음 설정과 함께 Fireball을 실행합니다.
> java -Dfile.server.root="/app/files" -Dfile.client.root="/app/files" -jar fireball-0.0.1.final.jar
Fireball은 파일 업로드와 다운로드 두 가지 REST API를 제공합니다. 다음은 Fireball을 사용해 시스템을 구성한 예시입니다. 이 예시에서는 호스트 A(192.168.10.10)에 사용자 서비스가 설치되고, 호스트 B(192.168.100.10)와 C(192.168.100.11)에 Fireball서비스가 설치되었습니다. 사용자는 호스트 B에게 파일 교환 요청을 보냅니다. 이런 경우 호스트 B의 Fireball 서비스는 파일 클라이언트 역할이 되고 호스트 C의 Fireball은 파일 서버 역할이 됩니다. 다음 절에서는 이 구성 안에서 호스트 B와 C 사이에 파일 업로드와 다운로드를 수행하는 요청 예시를 설명합니다.
다음과 같은 간단한 HTTP 요청을 통해 호스트 B에서 C로 파일을 업로드 할 수 있습니다. 예시에서는 호스트 B의 source.dat 파일을 호스트 C의 /parent/destination.dat 파일로 업로드합니다. 각 파일 경로는 설정된 루트 경로를 기준합니다. 만약 /parent 디렉토리가 존재하지 않는 경우 자동으로 디렉토리를 생성하고 destination.dat 파일이 이미 존재하는 경우 기존 파일을 덮어쓰며 새로운 파일을 생성합니다.
Method: POST
URL: http://192.168.100.10:58080/api/upload
Content-Type: application/json
{
"source": {
"file": "source.dat"
},
"destination": {
"ip": "192.168.100.11",
"port": 50711,
"file": "/parent/destination.dat"
}
}
다음과 같은 간단한 HTTP 요청을 통해 호스트 C에서 B로 파일을 다운로드 할 수 있습니다. 예시에서는 호스트 C의 source.dat 파일을 호스트 B의 /parent/destination.dat 파일로 다운로드합니다. 각 파일 경로는 설정된 루트 경로를 기준합니다. 만약 /parent 디렉토리가 존재하지 않는 경우 자동으로 디렉토리를 생성하고 destination.dat 파일이 이미 존재하는 경우 기존 파일을 덮어쓰며 새로운 파일을 생성합니다.
Method: POST
URL: http://192.168.100.10:58080/api/download
Content-Type: application/json
{
"source": {
"ip": "192.168.100.11",
"port": 50711,
"file": "source.dat"
},
"destination": {
"file": "/parent/destination.dat"
}
}
Fireball은 커스텀 HTTP 응답 헤더(“Error-No”, “Error-Message”)를 사용해 상세한 오류 상태를 공유합니다.
Error-No: 1
Error-Message: The file does not exist.
Fireball은 대용량 파일을 일련의 작은 청크(5MB)로 분할하여 전송합니다. 수신측에서는 하나의 청크를 네트워크로부터 수신하여 메모리에 버퍼링한 다음 한 번에 파일로 씁니다. 이를 위해 다음과 같은 애플리케이션 메시지 프로토콜을 구성합니다.
“빠르다”라는 개념은 “불타다”라는 개념과 잘 어울립니다. "파일"을 전송한다는 개념은 "볼"을 던진다는 개념과 역시 잘 어울린다고 생각했습니다. 여러가지 공 중에서 농구공을 선택한 이유는 제가 그것을 좋아하기 때문이기도 하지만 프로젝트가 가진 개념을 잘 표현하기 때문입니다. 농구공은 야구공이나 다른 공들에 비해 무겁습니다. 그래서 빨리 멀리 던지기가 쉽지 않죠. Fireball 프로젝트는 이 농구공 같은 대용량 파일을 불타오르듯이 빠르게 전송해 보고자 합니다.
Fireball 프로젝트는 다음 작업들을 우선적으로 계획하고 있습니다.
사용 중에 문제가 있거나 더 나은 개선 포인트가 있다면 저에게 언제든지 알려주십시요. 저는 당신이 필드에서 겪는 문제나 필요에 대해 더 알고 싶고, 그에 맞게 Fireball 서비스를 더 개선하고 싶습니다. 그리고 저는 유사한 형태의 작업이나 협업이 필요하다면 전세계 어느 곳에서든 풀타임 잡으로 일할 수 있습니다. (리모트잡도 가능합니다)