スキップしてメイン コンテンツに移動

Goワーカー・パイプライン設計

Goワーカー・パイプライン設計

Go応用と非同期パターンの基礎

Go言語は軽量なゴルーチンとチャネルを備えており、非同期パターンを簡潔に実装できます。典型的な例として、複数のタスクを同時に実行し、結果をチャネルで受け取る「ゴルーチン+チャネル」パターンがあります。これにより、I/O待ちや計算負荷を分散し、スレッド数を増やさずに高いスループットを実現できます。

func worker(id int, jobs <-chan int, results chan<- int) {
    for n := range jobs {
        results <- n * n
    }
}

上記のように、ジョブをチャネルに投入し、複数のworkerが並行して処理することで、非同期処理を安全に行えます。

Worker PoolとPipelineで実現する効率化

Worker Poolは固定数のゴルーチンを再利用し、タスクを順次処理することでリソースを最適化します。Pipelineは複数段階の処理をチェーン化し、各段階が独立したWorker Poolとして動作します。これにより、データフローを可視化しやすく、スケーラビリティと効率化が同時に達成できます。

func pipeline() {
    jobs := make(chan int, 100)
    results := make(chan int, 100)

    for w := 0; w < 5; w++ {
        go worker(w, jobs, results)
    }

    for i := 0; i < 100; i++ {
        jobs <- i
    }
    close(jobs)

    for i := 0; i < 100; i++ {
        fmt.Println(<-results)
    }
}

この構造は、CPUバウンドとI/Oバウンドの両方に対して有効で、スケーラビリティを高めつつ、コードの可読性も保ちます。

Fan-out/Fan-inとContext伝播でスケーラビリティを高める

Fan-out/Fan-inパターンは、1つの入力を複数のゴルーチンに分散し、結果を集約する手法です。Contextを利用すると、キャンセルやタイムアウトを全ゴルーチンに伝播させ、リソースリークを防止できます。

func fanOut(ctx context.Context, urls []string) []string {
    var wg sync.WaitGroup
    results := make(chan string, len(urls))

    for _, url := range urls {
        wg.Add(1)
        go func(u string) {
            defer wg.Done()
            req, _ := http.NewRequestWithContext(ctx, "GET", u, nil)
            resp, err := http.DefaultClient.Do(req)
            if err != nil { return }
            body, _ := io.ReadAll(resp.Body)
            results <- string(body)
        }(url)
    }

    go func() {
        wg.Wait()
        close(results)
    }()

    var out []string
    for r := range results {
        out = append(out, r)
    }
    return out
}

Contextを使うことで、全ゴルーチンが同時にキャンセルされ、スケーラビリティと安全性が向上します。

デザインパターンとしての並行パターンまとめ

Goでの並行設計は、デザインパターンと密接に結びついています。Worker Pool、Pipeline、Fan-out/Fan-inはそれぞれ「プロデューサ―/コンシューマー」「ストリーム」「分散処理」のパターンに対応し、Context伝播は「キャンセル/タイムアウト」のデコレータとして機能します。これらを組み合わせることで、スケーラビリティと効率化を両立させる堅牢なアーキテクチャが構築できます。

実際のプロダクションコードでは、これらのパターンを適切に選択し、テストとモニタリングを組み合わせることで、安定した高性能サービスを提供できます。

この記事はAIによって作成されました。

コメント