對 Go 中長時間運行 io.Reader 和 io.Writer 的操作測算進度和估算剩餘時間

來源:互聯網
上載者:User
這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。![img](https://raw.githubusercontent.com/studygolang/gctt-images/master/reader-writer/1_YfQ0FQIK4l6NMW3wsl9NNw.jpeg)每當我們在使用類似 io.Copy 和 ioutil.ReadAll 的工具時,比如我們正在從 http.Response 主體讀入或者上傳一個檔案,我們會發現這些方法將一直堵塞,直到整個過程完成,哪怕耗時數十分鐘甚至是小時——而且我們沒有辦法來查看進度,以及計算出完成所需剩餘時間的估測值。本文很長,不想深究瞅這裡:這篇文章最終導向 progress 包,你可以在自己的項目中自由使用——https://github.com/machinebox/progress考慮到 io.Reader 和 io.Writer 都是介面,我們可以封裝它們並且攔截 Read 和 Write 方法,捕獲實際已經通過它們的位元組數。通過一些簡單的數學計算,我們可以計算出已完成部分所佔的比例。再多上一點數學計算,我們甚至可以估測整個過程還剩餘多少時間,假設傳輸串流是相對一致的話。## 封裝 Reader一個新的 Reader 類型只需要包含另一個 io.Reader , 並且調用它的 Read 方法來擷取返回前讀到的位元組數。為了保證 reader 可以在並發環境中安全使用(在這個例子中至關重要),我們可以使用 atomic.AddInt64 作為安全的計數器。```go// Reader :計數通過它讀取的位元組數。type Reader struct {r io.Readern int64}// NewReader 返回一個可以計數通過它讀取到位元組數的// Readerfunc NewReader(r io.Reader) *Reader {return &Reader{r: r,}}func (r *Reader) Read(p []byte) (n int, err error) {n, err = r.r.Read(p)atomic.AddInt64(&r.n, int64(n))return}// N 表示目前為止讀取到的位元組數func (r *Reader) N() int64 {return atomic.LoadInt64(&r.n)}```試試看你能不在自己寫出 Writer 的計數部分,兩者很類似。由於方法 N 返回( 基於 atomic.LoadInt64 的安全調用)讀取到的位元組數,我們能在任意時刻使用另一個 goroutine 調用它,從而擷取當前狀況。## 擷取總共的位元組數為了計算百分比,我們需要知道總數是多少——我們預期讀取多少位元組?上傳檔案時,我們能夠利用作業系統擷取檔案大小。```goinfo, err := os.Stat(filename)if err != nil {return errors.Wrap(err, "cannot get file info")}size := info.Size(```在 HTTP 環境中,你可以藉助下面這些代碼來擷取 Content-Length 前序值。```gocontentLengthHeader := resp.Header.Get("ContentLength")size, err := strconv.ParseInt(contentLengthHeader, 10, 64)if err != nil {return err}```如果 Content-Length 前序是空的(這有可能),那麼就無法判斷進度或者估計剩餘時間。在其他狀況下,你也會需要弄清楚如何擷取位元組總數。## 計算百分比現在我們可以計算已經被處理的位元組數所佔百分比:```gofunc percent(n, size float64) float64 {if n == 0 {return 0}if n >= size {return 100}return 100.0 / (size / n )}```我們需要把值轉換為 float64 從而避免早期的向下取整。如果需要整數級精度的話我們依然可以把結果向下取整。## 估算剩餘時間有一個非常簡單的方法:求出讀取 X 位元組所需時間,然後乘以剩餘的位元組數。舉個例子,如果耗時 10 秒完成了 50% 的操作,那麼就可以假設仍需要 10 秒來完成整個任務;總耗時 20 秒。這並不絕對精確,但大多時候都可以給出一個可採用的倒計時。代碼就在下面,但不需要擔心你可能理解不了 —— 閱讀我們的 package 下面的詳細資料可以幫到你。```go// 開始時...started := time.Now()// 每次我們想查看時...ratio := n / sizepast := float64(time.Now().Sub(started))total := time.Duration(past / ratio)estimated := started.Add(total)duration := estimated.Sub(time.Now())```- `ratio` — 已經完成位元組數所佔的百分比- `past` — 從開始到現在的耗時- `total` — 基於已完成的百分比 ratio 和相應耗費的時間,從而得出的預計總耗時- `estimated` — 預測的結束時間點- `duration` — 預測距離完成還需要耗費的時間## 瀏覽 progess 包![img](https://raw.githubusercontent.com/studygolang/gctt-images/master/reader-writer/1_zjDaQfSU9YYY4WIz0K5CxA.png)我們熱愛開源,所以我們封裝了所有代碼到一個 [package](https://github.com/machinebox/progress) 中以方便您的使用。它也支援 io.EOF 和其他你知道的可能會在操作時發生的錯誤。### 小助手我們還添加了一個小助手,它可以給你一個進度上的 go channel 來周期性報告。 你可以開啟一個新的 goroutine 並列印進度,或更新進度,這取決於您的用例。```goctx := context.Background()// 得到一個 reader 和位元組總數s := `Now that's what I call progress`size := len(s)r := progress.NewReader(strings.NewReader(s))// 開啟一個 goroutine 列印進度go func() {progressChan := progress.NewTicker(ctx, r, size, 1*time.Second)for p := range <-progressChan {fmt.Printf("\r%v remaining...", p.Remaining().Round(time.Second))}fmt.Println("\rdownload is completed")}()// 使用 readerif _, err := io.Copy(dest, r); err != nil {log.Fatalln(err)}```該 channel 會周期性的返回一個 [Progress](https://godoc.org/github.com/machinebox/progress#Progress) 結構體,該結構體有下列幾個方法協助你瞭解細節。- `Percent` — 擷取操作完成的百分比- `Estimated` —  `time.Time` 表示預期操作結束的時間點- `Remaining` — 一個 `time.Duration` 變數標識剩餘時間channel 會在幾種情況下被關閉,例如操作已完成,或者操作被取消。[點擊文檔](https://godoc.org/github.com/machinebox/progress) 可以擷取 API 的最新詳細目錄### 樣本我們建立了一個 [example file downloader](https://github.com/machinebox/progress/blob/master/example/download/main.go) 來示範該 package 如何使用。## 還有什嗎?請嘗試我們的開源項目,提出問題,報告議題,提交重要的 PR 。## 什麼是 Machine Box ?![img](https://raw.githubusercontent.com/studygolang/gctt-images/master/reader-writer/1_GPdHUaxzqp2dJYd0l_hwcA.jpeg)[Machine Box](https://machinebox.io/?utm_source=blog&utm_medium=medium&utm_campaign=matblog) 把先進的機器學習技術放到 Docker 容器中,以便讓開發人員可以更輕鬆的整合自然語言處理,面部檢測,對象識別等技術到你自己的應用中。該技術是按比例構建,所以當你的應用擴大時只需要添加更多同級的 box 。噢,而且它比雲端服務廉價的多(可能還會更好)……而且你的資料也不會離開你自己的基礎裝置。[玩一玩](https://machinebox.io/docs/facebox/teaching-facebox) , 並且請告知我們您寶貴的意見。

via: https://blog.machinebox.io/measuring-the-progress-of-long-running-io-reader-and-io-writer-operations-in-go-ba26b204a507

作者:Mat Ryer 譯者:sunzhaohao 校對:rxcai

本文由 GCTT 原創編譯,Go語言中文網 榮譽推出

本文由 GCTT 原創翻譯,Go語言中文網 首發。也想加入譯者行列,為開源做一些自己的貢獻嗎?歡迎加入 GCTT!
翻譯工作和譯文發表僅用於學習和交流目的,翻譯工作遵照 CC-BY-NC-SA 協議規定,如果我們的工作有侵犯到您的權益,請及時聯絡我們。
歡迎遵照 CC-BY-NC-SA 協議規定 轉載,敬請在本文中標註並保留原文/譯文連結和作者/譯者等資訊。
文章僅代表作者的知識和看法,如有不同觀點,請樓下排隊吐槽

309 次點擊  

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.