いろいろ備忘録日記

主に .NET とか Go とか Flutter とか Python絡みのメモを公開しています。

Goメモ-92 (/dev/null のように出力を捨てる io.Writer)(ioutil.Discard)

概要

たまに、io.Writer が必要なのだけれども、出力データ自体は必要ないので捨てたいときがあります。

例えば、複数のURLに対して HTTP GET を並行で処理した場合の処理時間が知りたいときとか。

そういうときに便利な io.Writer の実装が、ioutil.Discard って名前で用意されています。

この子は、関数ではなくて 変数 です。

golang.org

サンプル

書籍 プログラミング言語Go にあるサンプルと大して変わらないですが・・w

package fileio

import (
    "io"
    "io/ioutil"
    "log"
    "net/http"
    "os"
    "sync"
    "time"
)

// NullWriter -- ioutil.Discard のサンプルです.
//
// REFERENCES:
//   - https://golang.org/pkg/io/ioutil/#pkg-variables
//   - https://stackoverflow.com/a/25344458
func NullWriter() error {
    // ----------------------------------------------------------------
    // ioutil.Discard は、io.Writer を実装しているけど何もしません。
    // 処理を行う上で io.Writer が必要だが、その結果は必要ない場合などに利用します。
    // ----------------------------------------------------------------
    var (
        urls = []string{
            "https://www.alexa.com/topsites/", // このサイトは大きいので取得に時間がかかる
            "https://www.google.co.jp/",
            "https://golang.org/",
            "https://github.com/",
        }
    )

    var (
        logger = log.New(os.Stdout, "", 0)
        wg     sync.WaitGroup
        errCh  = make(chan error, len(urls))
    )

    var (
        fetch = func(wg *sync.WaitGroup, url string) {
            defer wg.Done()
            defer func(start time.Time) {
                logger.Printf("fetch: %-40s --> %v\n", url, time.Since(start))
            }(time.Now())

            var (
                client = http.Client{
                    Timeout: 1 * time.Second,
                }
            )

            resp, err := client.Get(url)
            if err != nil {
                errCh <- err
                return
            }

            defer func() {
                if err = resp.Body.Close(); err != nil {
                    errCh <- err
                }
            }()

            // 結果は必要ないので捨てる
            _, err = io.Copy(ioutil.Discard, resp.Body)
            if err != nil {
                errCh <- err
                return
            }
        }
    )

    start := time.Now()
    for _, url := range urls {
        wg.Add(1)
        go fetch(&wg, url)
    }

    wg.Wait()
    close(errCh)

    for e := range errCh {
        logger.Printf("Error: %v\n", e)
    }

    logger.Printf("elapsed: %v\n", time.Since(start))

    return nil
}

実行すると以下のようになります。

$ make run
ENTER EXAMPLE NAME: fileio_null_writer
[Name] "fileio_null_writer"
fetch: https://github.com/                      --> 139.5146ms
fetch: https://www.google.co.jp/                --> 184.9419ms
fetch: https://golang.org/                      --> 275.8163ms
fetch: https://www.alexa.com/topsites/          --> 1.0000945s
Error: Get "https://www.alexa.com/topsites/": context deadline exceeded (Client.Timeout exceeded while awaiting headers)
elapsed: 1.0000945s


[Elapsed] 1.0000945s

参考情報

stackoverflow.com

プログラミング言語Go (ADDISON-WESLEY PROFESSIONAL COMPUTING SERIES)

プログラミング言語Go (ADDISON-WESLEY PROFESSIONAL COMPUTING SERIES)


過去の記事については、以下のページからご参照下さい。

  • いろいろ備忘録日記まとめ

devlights.github.io

サンプルコードは、以下の場所で公開しています。

  • いろいろ備忘録日記サンプルソース置き場

github.com

github.com

github.com