Mã nguồn tải file max speed sử dụng Goroutine (GGet)


  • Trùm cuối

    Github

    git clone https://github.com/ashwinGokhale/GGet

    Giới thiệu

    GGet là mã nguồn tăng tốc tải file được viết bởi Go. Mặc định sử dụng nhiều threads/goroutines nhất có thể tận dụng tài nguyên CPU để tải file một cách nhanh nhất. Bạn có thể quy định tên file và số threads cần sử dụng.

    6f8010a8-df60-4e55-9f21-fd44dc01824d-image.png

    Hướng dẫn sử dụng

    gget [url] [flags]

    Flags:
    -f, --filename string Tên file lưu xuống
    -h, --help help
    -s, --single-threaded Sử dụng 1 thread duy nhất
    -t, --threads int Số thread muốn tải cùng lúc

    Phân tích mã nguồn

    singleThreaded

    func (dl *Downloader) singleThreaded(startTime time.Time) error {
    	Info("Starting single threaded download...")
    	req, err := http.NewRequest("GET", url, nil)
    	if err != nil { return err }
    	resp, err := dl.client.Do(req)
    	if err != nil { return err }
    	defer resp.Body.Close()
    	body, err := ioutil.ReadAll(resp.Body)
    	if err != nil { return err }
    	file, err := os.OpenFile(dl.filename, os.O_WRONLY|os.O_CREATE, 0666)
    	if err != nil { return err }
    	defer file.Close()
    	Info("Writing to %s", dl.filename)
    	file.Write(body)
    	Info("Downloaded %d bytes to %s in %s",len(body), dl.filename, time.Now().Sub(startTime))
    	return nil
    }
    

    threadedDownload

    func (dl *Downloader) threadedDownload(length int, startTime time.Time) error {
    	Info("Starting threaded download...")
    	size := length / dl.threads
    	remainder := length % dl.threads
    	Info("Downloading %s on %d threads", dl.filename, dl.threads)
    	wg := &sync.WaitGroup{}
    	for i := 0; i < dl.threads; i++ {
    		wg.Add(1)
    
    		start := i * size
    		end := (i+1) * size
    
    		if i == dl.threads-1 {
    			end += remainder
    		}
    
    		Info("Starting thread %d", i)
    		go func(start, end, i int) error {
    			req, err := http.NewRequest("GET", url, nil)
    			if err != nil {
    				wg.Done()
    				return err
    			}
    			byteRange := fmt.Sprintf("bytes=%d-%d", start, end-1)
    			req.Header.Add("Range", byteRange)
    			resp, err := dl.client.Do(req)
    			if err != nil {
    				wg.Done()
    				return err
    			}
    			defer resp.Body.Close()
    			Info("Thread: %d Reading response body", i)
    			body, err := ioutil.ReadAll(resp.Body)
    			if err != nil {
    				wg.Done()
    				return err
    			}
    			file, err := os.OpenFile(dl.filename, os.O_WRONLY|os.O_CREATE, 0666)
    			if err != nil {
    				wg.Done()
    				return err
    			}
    			defer file.Close()
    			io.Copy(file, resp.Body)
    			Info("Thread: %d writing bytes %d - %d", i, start, end)
    			file.WriteAt(body, int64(start))
    			wg.Done()
    			Info("Thread: %d done", i)
    			return nil
    		}(start, end, i)
    	}
    	wg.Wait()
    	Info("Downloaded %s in %s", dl.filename, time.Now().Sub(startTime))
    	return nil
    }
    

    Ở hàm singleThreaded chúng ta chỉ cần gửi http request và nhận dữ liệu trả về thông qua ioutil sau đó ghi vào file

    Ở hàm threadedDownload thì phức tạp đôi chút,:

    • size := length / dl.threads sử dụng để chia đều kích thước file cần tải cho mỗi threads
    • go func(start, end, i int) để tạo các goroutines tương ứng và chạy đồng thời, độc lập
    • file.WriteAt(body, int64(start)) để ghi số byte đọc được vào chính xác offset trong file
    • wg.Done() để thông báo kết thúc goroutine hiện tại trong WaitGroup

    Quá đơn giản phải không các bạn? 😁


  • Trùm cuối



.
DMCA.com Protection Status