golang併發下載多個檔案
阿新 • • 發佈:2019-02-16
背景說明
假設有一個分散式檔案系統,現需要從該系統中併發下載一部分檔案到本地機器。
已知該檔案系統的部分節點ip, 以及需要下載的檔案fileID列表,並能通過這些資訊來拼接下載地址。
其中節點ip列表儲存在xx_node.txt, 要下載的fileID儲存在xx_fileID.txt中。
程式碼示例
package main
import (
"bufio"
"flag"
"fmt"
"io"
"math/rand"
"net/http"
"os"
"time"
)
var (
clustername = flag.String("clustername" , "c1", "download clustername")
)
// 逐行讀取檔案內容
func ReadLines(fpath string) []string {
fd, err := os.Open(fpath)
if err != nil {
panic(err)
}
defer fd.Close()
var lines []string
scanner := bufio.NewScanner(fd)
for scanner.Scan() {
lines = append(lines, scanner.Text())
}
if err := scanner.Err(); err != nil {
fmt.Fprintln(os.Stderr, err)
}
return lines
}
// 實現單個檔案的下載
func Download(clustername string, node string, fileID string) string {
nt := time.Now().Format("2006-01-02 15:04:05")
fmt.Printf("[%s]To download %s\n", nt, fileID)
url := fmt.Sprintf("http://%s/file/%s" , node, fileID)
fpath := fmt.Sprintf("/yourpath/download/%s_%s", clustername, fileID)
newFile, err := os.Create(fpath)
if err != nil {
fmt.Println(err.Error())
return "process failed for " + fileID
}
defer newFile.Close()
client := http.Client{Timeout: 900 * time.Second}
resp, err := client.Get(url)
defer resp.Body.Close()
_, err = io.Copy(newFile, resp.Body)
if err != nil {
fmt.Println(err.Error())
}
return fileID
}
func main() {
flag.Parse()
// 從檔案中讀取節點ip列表
nodelist := ReadLines(fmt.Sprintf("%s_node.txt", *clustername))
if len(nodelist) == 0 {
return
}
// 從檔案中讀取待下載的檔案ID列表
fileIDlist := ReadLines(fmt.Sprintf("%s_fileID.txt", *clustername))
if len(fileIDlist) == 0 {
return
}
ch := make(chan string)
// 每個goroutine處理一個檔案的下載
r := rand.New(rand.NewSource(time.Now().UnixNano()))
for _, fileID := range fileIDlist {
node := nodelist[r.Intn(len(nodelist))]
go func(node, fileID string) {
ch <- Download(*clustername, node, fileID)
}(node, fileID)
}
// 等待每個檔案下載的完成,並檢查超時
timeout := time.After(900 * time.Second)
for idx := 0; idx < len(fileIDlist); idx++ {
select {
case res := <-ch:
nt := time.Now().Format("2006-01-02 15:04:05")
fmt.Printf("[%s]Finish download %s\n", nt, res)
case <-timeout:
fmt.Println("Timeout...")
break
}
}
}
小結
下載時沒有用到預設的http Client, 並指定了超時時間;
下載檔案時呼叫了系統呼叫, goroutine會被掛起;
下載檔案完成後會喚醒被掛起的goroutine, 該goroutine執行完後面的程式碼後便退出;
全域性超時控制,超時後主執行緒退出。