Я пытаюсь улучшить производительность загрузчика, который я реализовал в golang. Я думаю, что у меня проблемы с использованием памяти, потому что программа застряла, когда я пытаюсь загрузить большой файл, например, 1 ГБ или больше. Я использовал его, чтобы загрузить файлы около 100 МБ и 300 МБ, и все было в порядке. Загрузчик используется на сервере, который представляет заголовок Accept-Ranges. Ниже я покажу вам реализацию и часть основного, но сначала позвольте мне объяснить вам.
Accept-Range: bytes
В этой реализации я создал http.Client для установки диапазона заголовка с той частью файла, которую я запрашиваю, после этого я сделал запрос. Чтобы сохранить ответ на этот запрос, я создал временный файл и скопировал ответ непосредственно в этот файл. Идея состоит в том, чтобы избежать копирования всего ответа в памяти. Это реализация:
func DownloadPart(wg *sync.WaitGroup, tempName string, url string, part string) {
//setting up the client to make the request
client := http.Client{}
request, err := http.NewRequest("GET", url, nil)
//setting up the requests
request.Header.Set("Range", part)
response, err := client.Do(request)
checkError(err, "fatal")
defer response.Body.Close()
//creating the temporary file and copying
// the response to it
file, err := os.Create(tempName)
checkError(err, "panic")
defer file.Close()
_, err = io.Copy(file, response.Body)
checkError(err, "fatal")
defer wg.Done()
}
Эта функция вызывается в различных процедурах, поэтому я использовал WaitGroup для уменьшения счетчика, когда заканчивается gorputine, чтобы загрузить часть файла. После завершения всех этих подпрограмм я объединяю временные файлы в один файл. Это реализация функции соединения
func joinFiles(name string) {
finalFile, err := os.OpenFile(name, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0644)
if err != nil {
log.Panicln(err.Error())
}
defer finalFile.Close()
files, err := ioutil.ReadDir(".")
for _, f := range files {
tempData, err := ioutil.ReadFile(f.Name())
if err != nil {
log.Panicln(err.Error())
}
if f.Name() != finalFile.Name() {
finalFile.Write(tempData)
os.Remove(f.Name())
}
}
}
Теперь я покажу вам часть основной функции, которая использует эти функции
//start, end and rest are used to set the Range header in the requests
//threads are the number of goroutines to used in the download
var wg sync.WaitGroup
wg.Add(threads)
//initializing the goroutines
for i := 0; i < threads; i++ {
part := fmt.Sprintf("bytes=%d-%d", start, end)
start = end + 1
if i == threads-1 {
end = end + step + rest
} else {
end = end + step
}
go tools.DownloadPart(&wg, fmt.Sprintf("%d.temp", i), url, part)
}
wg.Wait()
log.Println("Joining files...")
joinFiles(name)
Если есть способ улучшить эту реализацию