goの終点への接続数を制限する


私は最近、このパターンを二度実装していることに気づいた.その結果、私はそれを共有すると思いました.
まず、いくつかの背景.私は理由のためにウェブクローラを書きました.クローラは、Webページ内のリンクを見つけて、リンクをたどって、その終点で何があったかをダウンロードする試みルーチンを作成して、再発します.すぐに、これはシステムがオープンファイルハンドルを使い果たすクローラを実行させました.Goroutinesはとても強力で、シリアルダウンローダには逆の問題がありました.時間の状況で1つのためゆっくりと行くよりも、私たちはすぐに無料のファイルハンドルを使い果たした.JSON APIを介して発見されたリンクをダウンロードする別のクローラー型プログラムを書きました.
ブラウザは、リモートホストへの非常に多くの接続を開けない点に良い振舞いの例です.ウェブブラウザがどのように接続を制限するかについて説明している.それは私の行くプログラムを同じように振る舞うのは非常に簡単です.

func main() {
    ...
    for {
        go httpSave(currentURL, currentFileName)
    }
    ...
}

// httpSave saves u to file named name.
func httpSave(u, name string) {
    resp, err := http.Get(u)
    if err != nil {
        // Queue errors?
        log.Print(err)
        return
    }
    f, err := os.OpenFile(name, os.O_RDWR|os.O_CREATE, 0666)
    if err != nil {
        // Queue errors?
        log.Print(err)
    }
    defer f.Close()
    n, err := io.Copy(f, resp.Body)
    log.Printf("wrote %d bytes to %s", n, name)
}
上記HTTPSAVE関数は実際であり、メインはそれがどのように使用されるかについて部分的に省略されたコードです.
接続の数を制限する方法はたくさんありますが、ロックを使用するのではなくimoを理解するのは簡単です.私はまだロックを使用するつもりです、しかし、それは我々がチャンネルに制限しているホストをマップする地図への同期アクセスのためです.
私はGlobalsを使用していますが、より大きなアプリケーションのために、私はこの操作のために私のアプリ状態のまわりでデータを保持する構造体に地図とロックを加えます.
var domainLimit map[string]chan struct{}
var domainListMutex sync.Mutex

func main() {
    domainLimit = make(map[string]chan struct{})
    ...
}


func getDomainToken(u string) func() {
    u2, err := url.Parse(u)
    if err != nil {
        log.Print(err)
        return func() {}
    }

    domainListMutex.Lock()
    defer domainListMutex.Unlock()
    f, ok := domainLimit[u2.Host]
    if !ok {
        f = make(chan struct{}, 6) // Six connections per host.
        domainLimit[u2.Host] = f
    }
    f <- struct{}{}
    return func() {
        <-f
    }
}
これを使用するには、httpsave関数の先頭に1行を追加します.
func httpSave(u, name string) {
    defer getDomainToken(u)()
    ...
私がHTTP操作をする他の機能を持っているならば、私はその同じ一行を彼らのトップに加える必要があります.私の場合はしない.
私はそれが簡単で、それが動作するので、それが好きです.