2017-12-28 8 views
0

이미지 범위를 다운로드 할 응용 프로그램을 작성하려고합니다.대용량 파일 다운로드시 go 루틴이 멈 춥니 다

  • 130 116킬로바이트 이미지 (작품)
  • 50 개 500킬로바이트 이미지 (작품)
  • 130 5백킬로바이트 이미지 (결국 중단)
  • 230 116킬로바이트 이미지 (결국 중단)

이동 버전 go1 .9.2 darwin/amd64

package main 

import (
    "fmt" 
    "io" 
    "log" 
    "net/http" 
    "os" 
    "sync" 
) 

func main() { 

    var urls []string 

    // var smallImage = "https://s3.amazonaws.com/golangplayground/116kb.jpeg" //116kb 
    var largeImage = "https://s3.amazonaws.com/golangplayground/SampleJPGImage_500kbmb.jpg" //500kb 
    for i := 0; i < 130; i++ { 
     urls = append(urls, largeImage) 
    } 

    var wg sync.WaitGroup 
    wg.Add(len(urls)) 
    var inc = 0 
    for _, val := range urls { 
     inc += 1 
     go saveResourceFromURLToDisk(val, "./foo", &wg, inc) 
    } 
    wg.Wait() 
    fmt.Println("done.") 
} 

func saveResourceFromURLToDisk(url string, writeTo string, wg *sync.WaitGroup, inc int) error { 
    defer wg.Done() 

    response, err := http.Get(url) 
    if err != nil { 
     log.Fatal(err) 
     return err 
    } 
    defer response.Body.Close() 

    localPath := fmt.Sprintf("%s/%d", writeTo, inc) 
    file, err := os.Create(localPath) 
    if err != nil { 
     log.Fatal(err) 
     return err 
    } 
    defer file.Close() 

    _, err = io.Copy(file, response.Body) 
    if err != nil { 
     log.Fatal(err) 
     return err 
    } 

    fmt.Println(localPath) 
    return nil 
} 
+0

코드에 문제가없는 것 같습니다. S3의 GET 또는 동시 다운로드 제한을 누를 가능성이 있습니다. – abhink

+3

수백 개의 동시 연결을 열면 비효율적이며 일부 연결 또는 파일 설명자 제한에 따라 실행 중일 수 있습니다. – JimB

답변

1

이것은 아마 netw ork 문제. 웹 브라우저에 동일한 서버에 대해 열어야하는 세션 수에 제한이있는 이유가 있습니다.

동시에 여러 개의 TCP 세션을 열면 거의 모든 세션에서 패킷이 손실됩니다. 그런 다음 그들은 모두 같은 시간에 다시 시도하고 더 많은 패킷을 잃게됩니다. 그것은 잃는 것의 큰 더미입니다.

각 GET 요청을 열 때 약간의 지연 시간을 두거나 동일한 서버에서 4 - 8 회의 동시 다운로드를 제한하십시오.

0

저는 5 가지 요청에 대해 각각의 루틴을 버킷으로 버틴 것에 의해 Zan의 도움을 얻었습니다.이 방법을 사용하면 내가 만들고있는 열린 연결의 양을 줄이면서 병렬 처리를 이용할 수 있습니다.

조금은 순진하고 누구나 좀 더 우아한 해결책이 있는지 궁금합니다.

package main 

import (
    "fmt" 
    "io" 
    "log" 
    "net/http" 
    "os" 
    "sync" 
) 

func main() { 

    var urls []string 
    // var smallImage = "https://s3.amazonaws.com/golangplayground/116kb.jpeg" //116kb 
    var largeImage = "https://s3.amazonaws.com/golangplayground/SampleJPGImage_500kbmb.jpg" //500kb 
    for i := 0; i < 150; i++ { 
     urls = append(urls, largeImage) 
    } 

    var inc = 0; 
    for x:=0; x < len(urls)/5; x++ { 
     var wg sync.WaitGroup  
     for y:=0; y<5; y++ { 
      wg.Add(1)   
      go saveResourceFromURLToDisk(urls[x*y], "./foo", &wg, inc) 
      inc += 1 
     } 
     wg.Wait()         
    } 

    fmt.Println("done.") 
} 

func saveResourceFromURLToDisk(url string, writeTo string, wg *sync.WaitGroup, inc int) error { 
    defer wg.Done() 

    response, err := http.Get(url) 
    if err != nil { 
     log.Fatal(err) 
     return err 
    } 
    defer response.Body.Close() 

    localPath := fmt.Sprintf("%s/%d", writeTo, inc) 
    file, err := os.Create(localPath) 
    if err != nil { 
     log.Fatal(err) 
     return err 
    } 
    defer file.Close() 

    _, err = io.Copy(file, response.Body) 
    if err != nil { 
     log.Fatal(err) 
     return err 
    } 

    fmt.Println(localPath) 
    return nil 
} 
+0

모두 동일한 채널에서 청취하는 다섯 개의 골 루틴을 만듭니다. 다른 goroutine 또는 메인 프로그램은 채널에 URL 문자열을 보냅니다. 모든 URL을 보낸 후에 발신자는 채널을 닫아야합니다. goroutines는 닫을 때 종료해야합니다. 이미 보낸 것처럼 발신자는 대기 그룹에서 대기합니다. 이렇게하면 프로그램을 종료하기 전에 모든 URL을 가져오고 다운로드합니다. –

+0

고마워요 Zan 나는 그것을 시도 할 것입니다. – nwkeeley