Upgrade to Pro — share decks privately, control downloads, hide ads and more …

mercari.go #12 go-circuitbreakerのご紹介

Yasuharu Goto
December 06, 2019

mercari.go #12 go-circuitbreakerのご紹介

mercari.go #12 の登壇資料です。

Yasuharu Goto

December 06, 2019
Tweet

Other Decks in Programming

Transcript

  1. • Yasuharu Goto
 ◦ Backend Engineer ◦ 前職:ヤフー:Goでオブジェクトストレージ開発 • 株式会社メルペイ

    (2019年9月1日-)
 ◦ ID Platformチーム所属 ◦ 認証認可Microservicesの開発 
 小野マトペ (ono_matope)
 2
  2. Failures in Microservices Architecture
 • Microservices Architecture においては、依存サービスの障害は平常状態
 • 下位サービスの障害時


    ◦ リクエストのブロック時間が上位サービスに伝播 ◦ リトライすると下位サービスへの負荷をさらに高める悪循環 6 Microsrervice B Microsrervice C 10s Latency 10s+ Latency リトライによる 過負荷 Fail! Microsrervice A
  3. Circuit Breaker パターン
 Circuit Breaker = ブレーカー
 外部リクエストのエラーカウンタがしきい値を超えたら
 Circuit Openし、しばらくは処理を省略して


    即座にエラーを返す(Fail Fast)パターン
 
 ・障害時の連鎖的なレイテンシの増加を防げる
 ・リトライによる下位サービスの過負荷を緩和
 8 External Service Success Error (1) Error (2) Error (3) Trip Circuit Open! Trip Fail!
  4. CLOSED Circuit Breaker
 • CLOSED: リクエストが一定回数失敗したら OPEN に遷移
 • OPEN:

    一定の時間リクエストを止める (ブレーカーが落ちている状態)
 • HALF-OPEN: リクエストが失敗したら OPEN に戻る。一定回数成功したら CLOSED に復帰。
 9 External Service HALF-OPEN OPEN CLOSED
  5. github.com/sony/gobreaker
 13 • Execute メソッドに外部呼び出し処理を渡す
 ◦ 戻り値 error がnil →

    成功カウンタ++
 ◦ 戻り値 error がnon-nil → 失敗カウンタ++
 ◦ Circuit Open時 → 関数が実行されず ErrOpenState がreturn

  6. 課題
 • 現実のコードには様々な error が
 ◦ 障害起因のerror ▪ 失敗として扱いたい ◦

    障害と関係ないerror ▪ 「失敗」として扱うとFalse-Positive ▪ 「成功」として扱うとFalse-Negativeのケースも ◦ context起因のerror ▪ contexに対応した処理は、ユーザーがエラー終了させる可能性 • これらをCircuit Breakerに正しく伝えることは難しい
 22
  7. Doメソッド
 24 • Doメソッドが保護対象関数とともにcontext.Context を受け取る 
 • 基本的にはgobreakerと同じ 
 ◦

    戻り値 error が nil : 成功 ◦ 戻り値 error が non-nil : 失敗 ◦ ブレーカーが落ちたら関数は実行されずエラー
  8. 特徴1: 特定のエラーを無視・成功扱いに指定できる
 25 func Ignore(err) error 
 func MarkAsSuccess(err) error

    
 ラップしたエラーをサーキットブレーカに 「無視」させる。無視されたエラーは失敗 にも成功にもカウントされない。 
 ラップしたエラーをサーキットブレーカー に「成功」としてカウントさせる。 
 どちらもfuncの外にはアンラップしてreturnさ れる

  9. 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 (抜粋)
  10. 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 (抜粋)
  11. 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

  12. 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 (抜粋)

  13. 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 (抜粋)

  14. 39