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
mercari.go #12 go-circuitbreakerのご紹介
Search
Yasuharu Goto
December 06, 2019
Programming
6.2k
4
Share
Embed
Copy iframe code
Copy JS code
Copy link
Start on current slide
mercari.go #12 go-circuitbreakerのご紹介
mercari.go #12 の登壇資料です。
Yasuharu Goto
December 06, 2019
Other Decks in Programming
See All in Programming
気づいたらRubyで100作品 ー クリエイティブコーディングが生活の一部になるまで / 100 Ruby Sketches Later: How Creative Coding Became Part of My Life
chobishiba
3
570
そのテスト、説明できますか?~LWテスト戦略FW~のご紹介
nakahara
0
120
Language Server 使ってる? 〜VSCode と Zed の場合〜 / Are you using a Language Server? ~For VS Code and Zed~
handlename
0
780
AI時代の仕事技芸論 — ソフトウェア開発で「遊ぶように働く」職人的熟達のすすめ
kuranuki
2
680
Agentic UI
manfredsteyer
PRO
0
160
Developing with AI Agents — Codex, Claude Code & Cowork Practical Guide
x5gtrn
PRO
0
1.3k
DynamoDBには集計系のクエリがないけどなんとかしたい
musan
1
140
例外の正しい扱い方 そのエラー try-catchして大丈夫?
jinwatanabe
0
230
生成AI時代にこそ効くGo | Why Go Works in the Age of Generative AI
mom0tomo
8
3.2k
ユニットテストの先へ:テスト技法で要求・仕様を整理するJava開発実践 / Beyond_Unit_Testing_Practical_Java_Development_Techniques_for_Organizing_Requirements_and_Specifications
shimashima35
0
400
A2UI という光を覗いてみる
satohjohn
1
130
AI 時代のソフトウェア設計の学び方
masuda220
PRO
29
12k
Featured
See All Featured
Primal Persuasion: How to Engage the Brain for Learning That Lasts
tmiket
0
370
Principles of Awesome APIs and How to Build Them.
keavy
128
18k
How to build a perfect <img>
jonoalderson
1
5.6k
brightonSEO & MeasureFest 2025 - Christian Goodrich - Winning strategies for Black Friday CRO & PPC
cargoodrich
3
730
The State of eCommerce SEO: How to Win in Today's Products SERPs - #SEOweek
aleyda
2
11k
A designer walks into a library…
pauljervisheath
211
24k
Unsuck your backbone
ammeep
672
58k
Pawsitive SEO: Lessons from My Dog (and Many Mistakes) on Thriving as a Consultant in the Age of AI
davidcarrasco
0
160
Code Review Best Practice
trishagee
74
20k
The Language of Interfaces
destraynor
162
27k
Rebuilding a faster, lazier Slack
samanthasiow
85
9.5k
Why You Should Never Use an ORM
jnunemaker
PRO
61
9.9k
Transcript
@ono_matope go-circuitbreakerのご紹介
• Yasuharu Goto ◦ Backend Engineer ◦ 前職:ヤフー:Goでオブジェクトストレージ開発 • 株式会社メルペイ
(2019年9月1日-) ◦ ID Platformチーム所属 ◦ 認証認可Microservicesの開発 小野マトペ (ono_matope) 2
Go用 Circuit Breaker ライブラリ go-circuitbreaker を公開しました 3
https://github.com/mercari/go-circuitbreaker
Background 5
Failures in Microservices Architecture • Microservices Architecture においては、依存サービスの障害は平常状態 • 下位サービスの障害時
◦ リクエストのブロック時間が上位サービスに伝播 ◦ リトライすると下位サービスへの負荷をさらに高める悪循環 6 Microsrervice B Microsrervice C 10s Latency 10s+ Latency リトライによる 過負荷 Fail! Microsrervice A
Failures in Microservices Architecture 連鎖的な障害を防ぐため、障害サービスへのリクエストを遮断したい 7 Microsrervice C Fail! Fail
Fast Fail Fast Microsrervice B Microsrervice A
Circuit Breaker パターン Circuit Breaker = ブレーカー 外部リクエストのエラーカウンタがしきい値を超えたら Circuit Openし、しばらくは処理を省略して
即座にエラーを返す(Fail Fast)パターン ・障害時の連鎖的なレイテンシの増加を防げる ・リトライによる下位サービスの過負荷を緩和 8 External Service Success Error (1) Error (2) Error (3) Trip Circuit Open! Trip Fail!
CLOSED Circuit Breaker • CLOSED: リクエストが一定回数失敗したら OPEN に遷移 • OPEN:
一定の時間リクエストを止める (ブレーカーが落ちている状態) • HALF-OPEN: リクエストが失敗したら OPEN に戻る。一定回数成功したら CLOSED に復帰。 9 External Service HALF-OPEN OPEN CLOSED
Circuit Breaker =障害を検知して遮断するパターン
Circuit Breaker in Go 11
Circuit Breaker packages • github.com/rubyist/circuitbreaker • github.com/sony/gobreaker 12
github.com/sony/gobreaker 13 • Execute メソッドに外部呼び出し処理を渡す ◦ 戻り値 error がnil →
成功カウンタ++ ◦ 戻り値 error がnon-nil → 失敗カウンタ++ ◦ Circuit Open時 → 関数が実行されず ErrOpenState がreturn
現実はもう少し複雑 • Goの関数が返すエラーにはいろいろな種類がある ◦ 障害起因のエラー ◦ その他のエラー • Goコード上のerrorをすべて
「失敗」とみなしていいのだろうか? 14
現実の例 HTTPレスポンスボディ全体を読むコードを サーキットブレーカーで保護
リクエスト作成エラー:リクエストしてない。 無視したい
HTTPリクエスト失敗:「失敗」 リクエスト作成エラー:リクエストしてない。 無視したい
HTTPリクエスト失敗:「失敗」 HTTP 5xx系ステータス:「失敗」 リクエスト作成エラー:リクエストしてない。 無視したい
HTTPリクエスト失敗:「失敗」 HTTP 5xx系ステータス:「失敗」 HTTP 4xx系ステータス:サービス自体は生きてる。 場合によるが「成功」とみなしたい リクエスト作成エラー:リクエストしてない。 無視したい
レスポンスボディ読み込み失敗 => 「失敗」 HTTPリクエスト失敗:「失敗」 HTTP 5xx系ステータス:「失敗」 HTTP 4xx系ステータス:サービス自体は生きてる。 場合によるが「成功」とみなしたい リクエスト作成エラー:リクエストしてない。
無視したい
context ・contextを使ってユーザーがリクエストをキャ ンセルしたり、タイムアウト時間を指定したりす ると、リクエストがエラー終了する。 →障害とは無関係 ・ユーザーが発火可能。失敗として扱うとユー ザーがサーキットブレイカーを落とすことが出 来てしまう。
(gRPCではユーザーがキャンセルも タイムアウトも指定可能) https://www.irasutoya.com/2018/06/blog-post_972.html
課題 • 現実のコードには様々な error が ◦ 障害起因のerror ▪ 失敗として扱いたい ◦
障害と関係ないerror ▪ 「失敗」として扱うとFalse-Positive ▪ 「成功」として扱うとFalse-Negativeのケースも ◦ context起因のerror ▪ contexに対応した処理は、ユーザーがエラー終了させる可能性 • これらをCircuit Breakerに正しく伝えることは難しい 22
go-circuitbreaker 23 https://github.com/mercari/go-circuitbreaker
Doメソッド 24 • Doメソッドが保護対象関数とともにcontext.Context を受け取る • 基本的にはgobreakerと同じ ◦
戻り値 error が nil : 成功 ◦ 戻り値 error が non-nil : 失敗 ◦ ブレーカーが落ちたら関数は実行されずエラー
特徴1: 特定のエラーを無視・成功扱いに指定できる 25 func Ignore(err) error func MarkAsSuccess(err) error
ラップしたエラーをサーキットブレーカに 「無視」させる。無視されたエラーは失敗 にも成功にもカウントされない。 ラップしたエラーをサーキットブレーカー に「成功」としてカウントさせる。 どちらもfuncの外にはアンラップしてreturnさ れる
特徴2: context のキャンセル・タイムアウトを無視 26 • オプションで変更可能 • returnしたエラーはそのままDoの外側で受け取れる
外部リクエストのタイムアウトを失敗とみなす方法 27 • Do() の内側でcontextを派生してください。 • 内側のcontextがtimeoutしても外側はtimeoutしない→失敗としてカウント
実装上の工夫 28
29 https://docs.microsoft.com/ja-jp/azure/architecture/patterns/circuit-breaker • Circuit Breakerは成功・失敗カウンターと ク ローズ, オープン, ハーフオープン と3つの内
部状態を持つ • 単純な実装では分岐が多く複雑になる
State パターン in Go CBの内部状態 state を interface 定義。 30
type state interface { onEntry(cb *CircuitBreaker) // 状態開始時の処理 onExit(cb *CircuitBreaker) // 状態終了時の処理 onSuccess(cb *CircuitBreaker) // 成功時の処理 onFail(cb *CircuitBreaker) // 失敗時の処理 ready() bool // 処理を開始できるかの問い合わせ // ... } state.go (抜粋)
State パターン in Go CircuitBreaker は state を保持し、 特有の処理を移譲する。 (カウンタの増分は共通処理なので移譲していない)
31 func (cb *CircuitBreaker) Success() { cb.mu.Lock() defer cb.mu.Unlock() cb.cnt.incrementSuccesses() cb.state.onSuccess(cb) } func (cb *CircuitBreaker) Fail() { cb.mu.Lock() defer cb.mu.Unlock() cb.cnt.incrementFailures() cb.state.onFail(cb) } breaker.go (抜粋) type CircuitBreaker struct { state state cnt Counters // ... } breaker.go (抜粋)
state.go (抜粋) func (st *stateClosed) onEntry(cb *CircuitBreaker) { cb.cnt.resetFailures() //
... } func (st *stateClosed) onSuccess(cb *CircuitBreaker) {} func (st *stateClosed) onFail(cb *CircuitBreaker) { if cb.shouldTrip(&cb.cnt) { cb.setState(&stateOpen{}) } } 32 https://docs.microsoft.com/ja-jp/azure/architecture/patt erns/circuit-breaker Closed
func (st *stateOpen) onEntry(cb *CircuitBreaker) { // ... cb.clock.AfterFunc(timeout, cb.setStateWithLock(&stateHalfOpen{}))
} func (st *stateOpen) onSuccess(cb *CircuitBreaker) {} func (st *stateOpen) onFail(cb *CircuitBreaker) {} 33 https://docs.microsoft.com/ja-jp/azure/architecture/patterns/circuit-b reaker Open state.go (抜粋)
func (st *stateHalfOpen) onEntry(cb *CircuitBreaker) { cb.cnt.resetSuccesses() } func (st
*stateHalfOpen) onSuccess(cb *CircuitBreaker) { if cb.cnt.Successes >= cb.halfOpenMaxSuccesses { cb.setState(&stateClosed{}) } } func (st *stateHalfOpen) onFail(cb *CircuitBreaker) { cb.setState(&stateOpen{}) } 34 https://docs.microsoft.com/ja-jp/azure/architecture/patterns/circuit-b reaker Half Open state.go (抜粋)
State デザインパターン ・内部状態を型として表現 ・分岐の少ない保守性の高いコードに 35
まとめ 36
Go用Circuit Breakerを作りました 既存のアプリケーションに組み込みやすいはず 使ってみてください! 01 02 03 37
Thank you! 38
39