Client.Transport を変更せずに Go HTTP プロキシ経路を切り替える
同期化された RoundTripper ラッパーを使い、稼働中の http.Client フィールドを変更せずに、新しい設定済みプロキシトランスポートを公開します。
http.Client がすでに並行リクエストで共有されている場合は、その Transport フィールドを安定させたままにします。ラッパーは、以降の各ラウンドトリップがどの完全に設定されたトランスポートを受け取るかを同期できます:
type routeSlot struct {
mu sync.RWMutex
current http.RoundTripper
}
func (s *routeSlot) RoundTrip(r *http.Request) (*http.Response, error) {
s.mu.RLock()
current := s.current
s.mu.RUnlock()
return current.RoundTrip(r)
}
func (s *routeSlot) Set(next http.RoundTripper) {
s.mu.Lock()
s.current = next
s.mu.Unlock()
}net/http と sync を使い、クライアントを公開する前に初期化します:
slot := &routeSlot{current: initialTransport}
client := &http.Client{Transport: slot}
slot.Set(nextTransport)initialTransport と nextTransport は非 nil で、完全に設定され、並行性に対して安全なラウンドトリッパーでなければなりません。各プロキシトランスポートは公開前に構築し、リクエストが使用している間はその設定を変更しないでください。
ロックが保護するのはポインタのスナップショットだけなので、遅いリクエストがネットワーク交換の間ずっとルーティングロックを保持することはありません。前のスナップショットを使っているリクエストはそのトランスポート上で続行します。切り替えはそれを再実行せず、接続も移動しません。
古いトランスポートはアプリケーションの限られたライフサイクルの中で保持し廃止し、実行中の処理を完了させ、必要に応じてアイドルプールを閉じます。リダイレクトは切り替え後に別の RoundTrip を引き起こすことがあります。ワークフロー全体が 1 つの経路を必要とする場合は、この共有セレクタを途中で変更するのではなく、ワークフローの境界でその経路を割り当ててください。