本服务面向非中国大陆地区 · ZetaAPI.AI 的网站与控制台主要面向非中国大陆市场,注册与开通功能在你当前地区不可用。
ブログに戻る
安定性エンジニアリング 2026-04-10 8 min

デュアルチャネル分離でモデル可用率を 97% から 99.8% へ

単一供給チャネルの可用率の天井はどこか。18 か月分の本番データを振り返り、デュアルチャネル分離の実装と効果を分解します。

問題:単一チャネルの天井

2024 年末、私たちの中核モデル(Claude Sonnet、GPT-4o)は単一チャネルで上流プロバイダーに接続していました。月次可用率は 96.5%〜97.2% の間で揺れていました。十分そう?

計算すれば違うと分かります。97% の可用率は月に約 21 時間 の停止を意味します。私たちをインフラとして使うチームにとって、この数字は受け入れられません。

問題の根は私たちのコードではなく、上流プロバイダー自体の変動です。1 つのチャネルが落ちれば、すべてのトラフィックが影響を受けます。単一依存点はアーキテクチャの問題であって、運用の問題ではありません。

設計:デュアルチャネル分離

中核の発想はシンプルです。モデルごとに独立した供給チャネルを 2 本(Channel A / Channel B)維持し、互いに fallback とします。

ルーティングロジック

リクエストが来ると、ルーターは 3 つのことをします:

  1. 両チャネルのリアルタイム健康スコアを照会(直近 5 分のエラー率と遅延に基づく)
  2. 健康スコアの高いチャネルを優先
  3. 主チャネルが 5xx またはタイムアウトを返したら、即座に副チャネルへ再試行(ほとんどの場合ユーザーには無感知)
async function route(model: string, req: Request) {
  const [chA, chB] = await getChannels(model)
  const primary = chA.healthScore >= chB.healthScore ? chA : chB
  const fallback = primary === chA ? chB : chA

  try {
    return await primary.forward(req, { timeout: 30_000 })
  } catch (e) {
    // 主チャネル失敗、即切替——ユーザーには無感知
    metrics.record('failover', { model, from: primary.id })
    return await fallback.forward(req, { timeout: 30_000 })
  }
}

健康スコアの計算

各チャネルの健康スコアは 0〜100 のスライディングウィンドウ値です:

  • エラー率 重み 70% — 直近 5 分の 5xx / 総リクエスト数
  • 遅延 重み 30% — P95 遅延のベースラインからの乖離

スコアが 50 未満のチャネルは自動的に重みを下げ、3 分連続で 30 未満のチャネルは一時的にルーティングプールから外し、独立したハートビート探針で周期的に試し、回復後に再投入します。

結果:18 か月分の本番データ

デュアルチャネル分離の公開後、可用率は 99.81% に向上し、障害復旧時間は p95 4 分から p95 8 秒に短縮されました。大多数のユーザーはプロバイダー側の障害にまったく気づきませんでした。

指標単一チャネル(2024 Q4)デュアルチャネル(2025 Q3)
可用率97.0%99.81%
月平均停止時間21h1.4h
failover p95—8s

踏んだ落とし穴

  • 初期はタイムアウト予算を分離していなかった:副チャネルの timeout も 30 秒で、障害時に待ち時間が倍に。主 30 秒 / 副 20 秒に変えてから体感遅延が 35% 低下。
  • 健康スコアの遅延問題:5 分ウィンドウは突発障害への反応が遅く、後に 30 秒の「直近の失敗」高速シグナルを重ねました。
  • fallback ストーム:主チャネルの瞬間的な揺れで両側がポーリングし副チャネルを圧迫。クライアント側のレート制限 + バックオフで緩和。

次のステップ

デュアルチャネルは 単一ベンダー障害 をすでに解決しました。しかし私たちは依然として単一のネットワーク出口、単一の課金アカウント、単一のリージョンに依存しています。次のロードマップはこれらの層に分離を広げること——実装し終えたら 1 本書きます。

関連記事