Upgrade to Pro
— share decks privately, control downloads, hide ads and more …
Speaker Deck
Features
Speaker Deck
PRO
Sign in
Sign up for free
Search
Search
HTTP Retry の実装
Search
Shota Iwami
November 06, 2023
Programming
1.6k
0
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
HTTP Retry の実装
社内勉強会でのLTです
Shota Iwami
November 06, 2023
More Decks by Shota Iwami
See All by Shota Iwami
モノレポにおけるエラー管理 ~Runbook自動生成とチームメンションの最適化
biwashi
2
1k
OpenTelemetry の Log を使いこなそう
biwashi
7
1.9k
Monorepo Error Management: Automated Runbooks and Team-Targeted Alert Distribution
biwashi
2
1.1k
スタートアップ創業期を支えるオブザーバビリティ基盤のこれまでとこれから
biwashi
10
1.7k
モノレポ開発のエラー、誰が見る?Datadog で実現する適切なトリアージとエスカレーション
biwashi
7
1.8k
k6を活用した再現性・拡張性の高い負荷試験基盤の構築
biwashi
13
4.3k
Datadogマニアック機能活用術
biwashi
7
4.2k
feature flag と OpenTelemetry
biwashi
7
2.4k
OpenFeatureと自動生成を活用したフィーチャーフラグの宣言的集約管理
biwashi
20
7.4k
Other Decks in Programming
See All in Programming
AI時代の仕事技芸論 — ソフトウェア開発で「遊ぶように働く」職人的熟達のすすめ
kuranuki
2
680
ふつうのFeature Flag実践入門
irof
7
3.9k
Creating Composable Callables in Contemporary C++
rollbear
0
130
PHPで使える日時の表現と、その知り方 #frontend_phpcon_do
o0h
PRO
0
240
エージェンティックRAGにAWSで入門しよう!
har1101
8
1.6k
Strategic Design in the Frontend: Moduliths & Micro Frontends @DDDEurope
manfredsteyer
PRO
0
100
Signal Forms: Details & Live Coding @enterJS 2026 in Mannheim
manfredsteyer
PRO
0
140
ローカルLLMを使ってB2Bサービスを作っていての学び
yaotti
0
170
Go1.27で導入されるジェネリクスメソッドでできること
mackee
0
120
Contextとはなにか
chiroruxx
1
320
脅威をエンジニアリングの糧にして――現場編 / Turning Threats into Engineering Fuel — Field Edition
nrslib
0
280
依存関係から依存物へ―Dependencyという言葉の歴史をひも解く
j_lee
0
120
Featured
See All Featured
SEO Brein meetup: CTRL+C is not how to scale international SEO
lindahogenes
1
2.7k
Redefining SEO in the New Era of Traffic Generation
szymonslowik
1
340
Lightning talk: Run Django tests with GitHub Actions
sabderemane
0
200
The Art of Delivering Value - GDevCon NA Keynote
reverentgeek
16
2k
Put a Button on it: Removing Barriers to Going Fast.
kastner
60
4.3k
The Language of Interfaces
destraynor
162
27k
Music & Morning Musume
bryan
47
7.2k
The SEO identity crisis: Don't let AI make you average
varn
0
490
Effective software design: The role of men in debugging patriarchy in IT @ Voxxed Days AMS
baasie
0
410
Jess Joyce - The Pitfalls of Following Frameworks
techseoconnect
PRO
1
170
The World Runs on Bad Software
bkeepers
PRO
72
12k
From Legacy to Launchpad: Building Startup-Ready Communities
dugsong
0
230
Transcript
HTTP Retryの実装 AI事業本部 アプリ運用センター 岩見彰太
自己紹介 岩見 彰太 趣味:サックス・ドローン・カメラ AI事業本部 小売DX アプリ運用センター 22卒バックエンドエンジニア B_Sardine
事の発端 とあるAPIに対して特定のステータスコードの時に Exponential Backoffによるリトライを実装したい
最初こんな感じで実装してみた ということで…
client.Do(req)を実行する responseのステータスコードが429なら エラーを返す エラーだった時にリトライする cenkalti/backoff 用メソッド maxRetryNumberで設定 した回数だけリトライする
最初の1回目以降、Request Bodyが空だと怒られて Do がエラーになる 適当なサーバを立てて叩いて見ると…
こんな感じで、Bodyを入れ直すと通る Bodyの巻き戻し. 入れ直しが必要 先に結論
Requestの構造体を見てみる Bodyはio.ReadCloserというインターフェース型 今回入れた具象型はbytes.Buffer 複数回リトライする場合, これが複数回実行される bytes.Bufferの構造体を見てみる lastReadという読み切った場所を内部で保存してる なぜなくなる? リトライする場合は Request.Bodyを初期状態に戻す必要がある
つまり…
http.Clientの中のTrasport.roundTripがリトライ処理し てる ネットワークエラーとかになった時にリトライしてる rewindBodyというメソッドを呼んでる Request.Bodyを閉じる body, err := req.GetBody()でRequst.Bodyのコピーを取 り出す
取り出したものをもう一度reqに入れる rewindBodyの流れ 1. 2. 3. 実はnet/httpもリトライしてる コネクションの確立とかコネクションプールの管理を行なってる Transport
*bytes.Buffer <----- 今回の場合 *bytes.Reader *strings.Reader Request.Bodyの具象型が以下の時に GetBodyがセットされる io.NopCloser は何もしない io.ReadCloser
(Bodyで指定されているinterface型)を実装できる req.GetBody()は Request作成時に作られる
Transport.roundTripでも終了判定している リトライをする場合、呼び出し元のcontextを伝播す る必要がある 同じように判定する必要がある Contextの終了を確認する
レスポンス内容はio.ReadCloserの Response.Bodyから読み取れる 呼び出し側で最後まで読み取ってから Close しな いと keep-aliveの TCPコネクションが再利用さ れない レスポンスを返した後に接続を維持し、次のリク
エストを送るときにその接続を再利用して送ること ができる Keep-AliveはResponseが読み切られてCloseした タイミングで再利用できるようになる > Bodyを閉じるのは呼び出し側の責任である。 > デフォルトの HTTP クライアントの Transport は、Body を最後まで読んで閉じな いと、 HTTP/1.x の "keep-alive" TCP コネクションを再利用しないかもしれません。 Keep-Alive http.Response.Bodyを 読み切ってから閉じる
Transport 内部では、connectMethodKey(接続先) 単位で idel 状態のTCPコネクションや 確立待ちのキューが管理されている これがデフォルトの場合はDefaultTransportが使用される 90s: IdleConnTimeout(リクエストが終わってもコネクションが維持される時間) 2:
MaxIdleConnsPerHost(接続先ごとのKeep-Aliveの最大数) TransportでのTCPコネクションの管理
CloseIdleConnections()でコネクションプールを 一括で閉じることができる リトライ終了後に意図的に解放したい場合は使える (これを呼ばない場合、IdleConnTimeoutが過ぎると 自動的に解放される) コネクションプールを閉じる
リトライが必要となる場面(429 Too Many Requests)では、短期間で同じ宛先にリクエスト する場合 そうなると、 MaxIdleConnsPerHost だと少ない可能性が高い リトライしている期間中占有してしまい、解放まで時間がかかる可能性があるから 場合によっては専用の
Transport を作成した方がいい この場合、使わないのに IdleConnTimeout で設定されている時間分 Keep-Alive が保持 されて、コネクションリソースを消費してしまうので、リトライが終了した後は CloseIdleConnections を読んでコネクションプールを開放するべき (終了したらコネクションプールを閉じる) リトライ用のTransportを別で用意するべき?
ただリトライしたいだけなのに 考えること多くてめんどくさい… ここら辺まるっとやってくれる便利なライブラリないのか……
今まで説明してきたようなことが考慮されてる TransportはMaxIdleConnsPerHostを増やし て、リトライ後には閉じるようになっている net/httpと同じように使えて直感的 -> 結局これを使いました Hashicorp製 hashicorp/go-retryablehttp ありました
まとめ リクエストの内容(Request.Body)をリトライ前に巻き戻す Request.Context()の終了を確認する リトライ前にResponse.Bodyを全て読み切ってから閉じる デフォルトのTransportを使用してコネクションプール管理するか検討する hashicorp/go-retryablehttpの使用を検討する ちなみに、AWS SDK for GoとかでAPIコールする場合は、勝手にリトライしてくれるようになってる