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.9k
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
1.1k
OpenTelemetry の Log を使いこなそう
biwashi
7
2.2k
Monorepo Error Management: Automated Runbooks and Team-Targeted Alert Distribution
biwashi
2
1.2k
スタートアップ創業期を支えるオブザーバビリティ基盤のこれまでとこれから
biwashi
10
1.7k
モノレポ開発のエラー、誰が見る?Datadog で実現する適切なトリアージとエスカレーション
biwashi
7
1.8k
k6を活用した再現性・拡張性の高い負荷試験基盤の構築
biwashi
13
4.4k
Datadogマニアック機能活用術
biwashi
7
4.6k
feature flag と OpenTelemetry
biwashi
7
2.5k
OpenFeatureと自動生成を活用したフィーチャーフラグの宣言的集約管理
biwashi
20
7.6k
Other Decks in Programming
See All in Programming
What We Talk About When We Talk About XP
m_seki
2
330
新卒PdEのリアル
ryu1013
1
290
初心者DevRelとして参加者だった私が、DevRel Talks!#2に登壇するまでにしてきたこと
sokohirai
0
330
Hono + Inertia + React で LP を構築した話
oukayuka
2
200
「つくるAI」だけではバグは見つからない ~テストに必要な「見つけるAI」を分離させる戦略~
mfunaki
0
230
DynamoDBの基礎を振り返りながらベクトル検索機能を理解する
musan
3
260
LoopHub - ローカルで動く GitHub で、AI と共同開発
jugyo
0
390
一参加者から『中の人』へ 〜全通PHPerがブースに立って学んだ、カンファレンスを100倍楽しむコツ〜
wp_daisuke
0
110
ソフトウェアラスタライザ
fadis
1
780
ALB ログから Trace を気合で繋げる技術
fohte
7
860
FastAPI の並行処理モデルを完全に理解する
hoto17296
9
3.7k
LLMは4年分のCompose移行を再現できるのか?実プロダクト279件のXMLで探る自動化の境界線
makun
0
360
Featured
See All Featured
Measuring Dark Social's Impact On Conversion and Attribution
stephenakadiri
2
270
Code Review Best Practice
trishagee
74
20k
Fantastic passwords and where to find them - at NoRuKo
philnash
52
3.8k
Helping Users Find Their Own Way: Creating Modern Search Experiences
danielanewman
31
3.3k
Technical Leadership for Architectural Decision Making
baasie
3
550
AI Search: Implications for SEO and How to Move Forward - #ShenzhenSEOConference
aleyda
1
1.4k
Lessons Learnt from Crawling 1000+ Websites
charlesmeaden
PRO
1
1.6k
Are puppies a ranking factor?
jonoalderson
2
3.9k
Learning to Love Humans: Emotional Interface Design
aarron
275
41k
Building a Modern Day E-commerce SEO Strategy
aleyda
45
9.2k
"I'm Feeling Lucky" - Building Great Search Experiences for Today's Users (#IAC19)
danielanewman
230
23k
Practical Tips for Bootstrapping Information Extraction Pipelines
honnibal
25
2k
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コールする場合は、勝手にリトライしてくれるようになってる