信頼性パターン
分散システムの障害に対処するためのパターン
分散システムでは障害は避けられない。 これらのパターンは障害を検知・隔離・回復するための仕組み。
Circuit Breaker(サーキットブレーカー)
障害が発生しているサービスへの呼び出しを遮断し、 システム全体への影響を防ぐ。電気のブレーカーと同じ発想。
状態遷移図
🟢
Closed
通常動作
リクエストを通す
閾値超過
→
5回失敗など
🔴
Open
遮断中
即座にエラー返却
タイムアウト後
→
30秒後など
🟡
Half-Open
試行中
一部リクエストで確認
成功 → Closedへ
失敗 → Openへ
典型的な設定値
Failure Threshold
5回失敗でOpen
Reset Timeout
30秒後にHalf-Open
Success Threshold
3回成功でClosed
Retry(リトライ)
一時的な障害に対して再試行する。 ただし、指数バックオフを使わないと障害を悪化させる。
Exponential Backoff(指数バックオフ)
1回目
1秒
2回目
2秒
3回目
4秒
4回目
8秒
5回目
16秒(最大値でキャップ)
+ Jitter(ランダム)
全クライアントが同時にリトライすると「Thundering Herd」問題。 待機時間に±20%程度のランダムを加える。
リトライすべき
- • 503 Service Unavailable
- • 429 Too Many Requests
- • Connection timeout
- • Network unreachable
リトライすべきでない
- • 400 Bad Request
- • 401 Unauthorized
- • 404 Not Found
- • 非冪等な操作(注意が必要)
Timeout(タイムアウト)
応答がない場合に待機を打ち切る。 タイムアウトがないと、リソースが無限に待機し続ける。
タイムアウトの階層
Connect Timeout
接続確立まで(通常: 1-5秒)
Read Timeout
レスポンス受信まで(通常: 5-30秒)
Request Timeout
リクエスト全体(通常: 30-60秒)
タイムアウトバジェット
Client
30秒
→
API Gateway
25秒
→
Service A
20秒
→
Service B
15秒
ポイント: 下流のタイムアウトは上流より短くする。 上流でタイムアウトしても下流が動き続けるのは無駄。
Bulkhead(隔壁)
リソースを分離して、一部の障害が全体に波及するのを防ぐ。 船の隔壁(浸水しても他の区画は守る)と同じ発想。
隔壁なし
共有スレッドプール (100)
Service A
20使用
Service B
80使用!
Service C
0 待機
Service Bの遅延でプール枯渇 → 全体に影響
隔壁あり
Pool A (30)
Service A
20使用
Pool B (30)
Service B
30使用!
Pool C (40)
Service C
正常動作
Service Bの問題は他に影響しない
Rate Limiting(レート制限)
リクエストの流量を制限して、システムを過負荷から守る。
主なアルゴリズム
Token Bucket
🪣
• 一定レートでトークン追加
• リクエストでトークン消費
• バーストを許容
Leaky Bucket
🚿
• 一定レートで処理
• 超過はキューに蓄積
• 安定した流量
パターンの組み合わせ
Client Request
↓
Rate Limiter
過負荷防止
↓
Bulkhead
リソース分離
↓
Circuit Breaker
障害検知・遮断
↓
Retry + Timeout
一時障害対応
↓
Downstream Service
主な実装ライブラリ
Resilience4j
Java向け。Circuit Breaker, Retry, Rate Limiter, Bulkhead, Time Limiterを提供。
Polly
.NET向け。同様のパターンをfluent APIで提供。
Hystrix(非推奨)
Netflix製。メンテナンスモード。Resilience4jへの移行推奨。
Service Mesh
Istio/Linkerdでインフラレベルで実装。アプリ変更不要。