Go 言語は、同時実行プログラミングに非常に適したプログラミング言語であり、そのパフォーマンスは、同時実行性の高いサービスやアプリケーションを実装する際に十分に活用されます。日常の開発では、同時リクエスト インターフェイスや大量のデータの同時処理が必要なシナリオに遭遇することがありますが、この記事では、golang で同時リクエスト インターフェイスを実装する方法を紹介します。
実際の開発では、インターフェイスをリクエストして応答データを取得する必要がある次のようなシナリオに遭遇することがあります。
単一スレッドで複数のインターフェースをリクエストする必要がある場合、別のインターフェースをリクエストする前に 1 つのインターフェースリクエストを完了する必要があるため、プロセス全体が遅くなります。逆に、同時リクエスト インターフェイスを使用すると、複数のリクエストを同時に開始できるため、リクエストの効率が大幅に向上します。
Goroutine は、メインスレッドと並行して特別なスレッドで実行できる Go 言語の特別な関数です。複数のゴルーチンを同時に実行すると、同時に複数のインターフェースをリクエストし、リクエスト完了後にデータ統合処理を実行できます。 goroutine の同時使用は比較的簡単に実装でき、go キーワードを使用して実現できます。
実際の開発では、一部のコルーチンはより時間がかかり、結果を返すまでにさらに時間がかかる場合があります。この場合、コルーチンが結果を返して後続の処理を実行するのを待つ必要があります。現時点では、sync.WaitGroup を使用してゴルーチンの数を制御し、すべてのリクエストが応答結果を確実に受け取るようにする必要があります。
package main import ( "fmt" "io/ioutil" "net/http" "sync" ) var wg sync.WaitGroup // 声明一个sync.WaitGroup实例,用于协程控制 func main() { urls := []string{"https://www.baidu.com", "https://www.qq.com", "https://www.taobao.com", "https://www.jd.com", "https://www.mi.com"} // 通过遍历urls,启动goroutine for _, url := range urls { wg.Add(1) // 添加一个goroutine go getBody(url) } wg.Wait() // 等待所有goroutine结束 } // getBody用于获取传入url的响应结果,并打印。 func getBody(url string) { resp, err := http.Get(url) // 发起http GET请求 if err != nil { fmt.Println(err) return } defer resp.Body.Close() body, err := ioutil.ReadAll(resp.Body) if err != nil { fmt.Println(err) return } fmt.Printf("url: %s, contents: %s ", url, string(body)) wg.Done() // 相当于wg.Add(-1),标志该goroutine已经结束 }
上記のコードでは、最初にコルーチンの数を制御するための sync.WaitGroup インスタンスを宣言します。次に、main()
関数で、URL をトラバースすることによって複数のコルーチンが開始されます。同時に、コルーチンが開始されるたびに、wg.Add(1) メソッドが呼び出され、必要があることを示します。コルーチンが完了するまで待機します。この場合、WaitGroupに記録されている待機コルーチンの数がurlsのurl数となります。次に、go getBody(url)
行で、URL をリクエストするコルーチンを開始し、コルーチンが終了したときに wg.Done()
メソッドを呼び出します。これは、コルーチンが終了したことを示します。プロセスは終了しました。
最後に、wg.Wait()
が呼び出され、すべてのコルーチンが終了するまでメイン コルーチンが待機します。
実際の開発では、同時リクエスト インターフェイスをより適切に使用するのに役立ついくつかの詳細に注意を払う必要があります。
1. 同時実行数の制御
インターフェースを同時にリクエストする場合、特にインターフェースリクエストの数が比較的多い場合、同時実行数を制御する必要があります。リクエストがサーバーに多大なダメージを与えないようにするため、大きなプレッシャーがかかります。同時実行数を最大にするために最大値を設定できます。 golang のバッファ チャネルを使用して、同時実行の最大数を制御できます。
ch := make(chan struct{}, 5) // 声明一个缓冲通道,大小为5,控制并发数量为5 for _, url := range urls { ch <- struct{}{} // 把协程数量放在通道里 wg.Add(1) // 添加一个goroutine go func(url string) { defer wg.Done() getBody(url) <-ch // 从通道里取出一个值,表示这个协程已经结束 }(url) }
バッファ チャネルを宣言するプロセスで、バッファ サイズを 5 に設定します。これは、最大 5 つのゴルーチンを同時に実行できることを意味します。次に、URL を走査し、構造体の値をチャンネル。
ゴルーチンを開始するとき、同時に実行されるゴルーチンの最大数が 5 を超えないように、処理関数として func(url string)
を宣言し、その後 getBody を呼び出しました。 (url )
メソッド。ゴルーチンが終了すると、ゴルーチンが終了したことを示すシグナルをチャネル経由で放出します (<-ch
)。
2. リクエストのブロックを回避する
同時リクエスト インターフェイスを作成するときは、リクエストが長時間応答しない場合に通常発生するリクエストのブロックを回避する必要があります。この問題は、Golang の context.Context を使用して解決できます。リクエストがタイムアウトした場合は、ブロックされたリクエストをキャンセルします。
url := "https://httpstat.us/200?sleep=8000" ctx, cancel := context.WithTimeout(context.Background(), time.Millisecond*5000) // 告诉请求,5秒之后自动取消 defer cancel() req, err := http.NewRequestWithContext(ctx, "GET", url, nil) // 使用请求上下文 if err != nil { log.Fatal(err) } client := http.DefaultClient resp, err := client.Do(req) // 发起请求 if err != nil { log.Fatal(err) } if resp.StatusCode == http.StatusOK { contents, err := ioutil.ReadAll(resp.Body) if err != nil { log.Fatal(err) } fmt.Printf("%s ", contents) }
上記のコードでは、context.WithTimeout
メソッドを使用して、タイムアウトが 5 秒に設定されたリクエスト コンテキスト (例: http://httpstat.us/200?) を作成しました。 sleep=8000、このリクエストはデータを返すのに 8 秒かかります。次に、http.NewRequestWithContext メソッドを使用してリクエスト コンテキストを使用してリクエストを作成します。リクエストを送信するときは、http.DefaultClient
を使用してリクエストを開始します。最後に、レスポンスステータスコードが200の場合、レスポンスデータを出力します。
リクエストがタイムアウトすると、リクエスト リンクは直接シャットダウンされます。この時点で、「コンテキストの期限を超えました」というエラーが表示されます。
3. 繰り返しのリクエストを避ける
インターフェースをリクエストするとき、同じインターフェースに対する繰り返しのリクエストが発生する可能性があります。この場合、同じインターフェースに対する繰り返しのリクエストを避ける必要があります。貴重な時間とリソース。 Golang の sync.Map を使用すると、この問題を解決できます。
var m = sync.Map{} url := "https://httpbin.org/get" wg.Add(2) go doGet(url, &m, &wg) go doGet(url, &m, &wg) wg.Wait() func doGet(url string, m *sync.Map, wg *sync.WaitGroup) { _, loaded := m.LoadOrStore(url, true) // 表示url已经被请求过,如果已存在,则直接返回,否则返回false并储存 if loaded { fmt.Printf("url %s already requested. ", url) wg.Done() return } resp, err := http.Get(url) if err != nil { log.Fatal(err) } defer resp.Body.Close() contents, err := ioutil.ReadAll(resp.Body) if err != nil { log.Fatal(err) } fmt.Printf("%s ", contents) wg.Done() }
上記のコードでは、sync.Map を使用して、URL が 1 回だけリクエストされるようにしています。 doGet
コルーチンでは、m.LoadOrStore(url, true) を使用して URL がリクエストされたかどうかを判断します。リクエストされている場合、return
はコルーチンを直接終了します。それ以外の場合は、http.Get リクエストを開始し、応答データをログに出力します。最後に、wg.Done()
メソッドによってコルーチンが終了したことをマークします。
この記事では、golang を使用して同時リクエスト インターフェイスを実装する方法を紹介します。 goroutine 同時実行処理、WaitGroup コルーチン制御、およびバッファー チャネルを使用して、同時実行数を制御します。リクエスト コンテキストでタイムアウトを設定してリクエストのブロックを回避し、sync.Map を使用してリクエストの重複を回避します。これらのテクノロジーを使用することで、リクエスト インターフェイスの効率が大幅に向上し、コーディング効率とプログラミング エクスペリエンスが向上します。
以上がgolang 同時リクエスト インターフェイスの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。