API プロバイダーとのウォレット送金:リクエストキューを詰まらせない
プラットフォームと API プロバイダーの間で連鎖する送金呼び出しがリクエストキューをあふれさせる仕組みと、キュー・即時応答・結果のプッシュでプロバイダーが遅くてもプラットフォームを安定させる方法。
プラットフォームはユーザーのウォレットを預かり、API プロバイダーはプラットフォームが提供するサービスの裏側でサーバーを運用しています。ユーザーがあるプロバイダーのサービスを使う前に資金はプラットフォームからプロバイダーへ移り、使い終わると戻ってきます。多くの連携では、この送金を双方向に連鎖する同期 HTTP 呼び出しで作っています。負荷が上がるとこの連鎖が両側のリクエストキューを埋め、遅いプロバイダーが一つあるだけでプラットフォーム全体が巻き込まれます。プラットフォームの安定性が、他社のサーバーに左右されるべきではありません。
送金はリクエストの連鎖になっている
プラットフォームの worker は、プロバイダーからのコールバックも含めた往復の間ずっと占有されます。プロバイダーの worker は、そのコールバックを待つ間占有されます。お互いがお互いを待つので、トラフィックが増えると両方のプールが同時に埋まります。タイムアウトすると送金の状態がわからなくなり、状態のポーリングと再試行が始まります。冪等キーがなければ、再試行で二重に送金されることもあります。
遅いプロバイダー一つが全体の問題になるまで
ベンチマークではなく試算の例です(2026-11-09 作成)。毎秒 500 件の送金が 3 つのプロバイダーに分かれ、プラットフォームのプールは 200 worker です。使われる worker 数はリトルの法則で出せます。毎秒の送金数 × 1 件が worker を占有する秒数です。
| 連鎖する HTTP | キュー、即時受付、結果のプッシュ | |
|---|---|---|
| ユーザーのリクエストがプラットフォームの worker を占有する時間 | 20 ms + プロバイダー呼び出し 150 ms = 170 ms | 20 ms |
| 毎秒 500 件の送金で使われる worker 数 | 500 × 0.17 秒 = 85 | 500 × 0.02 秒 = 10 |
| 3 つのうち 1 つのプロバイダーが 5 秒のタイムアウトまで止まる | 約 900:200 のプールを使い切り、すべてのプロバイダーが失敗 | 変わらず 10:待つのはそのプロバイダーの送金だけ |
| 結果を待つ間のリクエスト | 保留中の送金ごとに 2 秒おきの状態ポーリング | なし。プロバイダーごとに数分おきのまとめた確認だけ |
変更後:すぐ受け付け、裏で処理し、結果をプッシュする
- プラットフォームはまずユーザーに応答する。 送金を処理中として台帳に書き、キューに入れてから応答します。ユーザーの画面には「処理中」と表示され、イベントが届くと自動で更新されます。
- ディスパッチャーが各プロバイダーへ送金を送る。 冪等キー、短いタイムアウト、プロバイダーごとに決まった枠数を付けます。プロバイダーは送金を記録した時点で 202 を返すので、同じキーでの再試行は常に安全です。
- コールバックしない。 リクエストにはプロバイダーが必要とするものをすべて署名付きで載せるので、プロバイダーが確認のためにプラットフォームを呼び返す必要はありません。
- 結果はプッシュで。 プロバイダーはそのプラットフォーム用のプライベートチャンネル、たとえば
private-partner-acmeにtransfer.settledを発行します。プラットフォームのバックエンドは WebSocket 接続を一つ保ち、プロバイダーの認可エンドポイントの署名で購読します。台帳を更新し、ユーザーのウォレットチャンネルにbalance.updatedを発行します。プロダクト間の残高同期と同じやり方です。 - 取りこぼしは一度の確認で。 プラットフォームは数分ごとに、各プロバイダーへまだ処理中の送金をまとめて一度だけ問い合わせます。送金ごとのポーリングはしません。
プロバイダーが不安定でもプラットフォームは安定させる
- プロバイダーごとに枠を分ける。 遅くなったプロバイダーが埋めるのは、ディスパッチャーの自分の枠だけです。ログイン、入金、ほかのプロバイダーの worker はそのまま残ります。
- 応答しないプロバイダーには送るのをやめる。 タイムアウトが続いたらそのプロバイダーのキューを止め、サービスを一時的に利用不可と表示し、少し待ってから再開します。送金は占有された接続ではなく、台帳の中で処理中として待ちます。
- WebSocket が切れても何も止まらない。 送金は受け付け続けます。接続が戻れば、まとめた確認でその間の分が処理されます。
- ユーザーがプロバイダーの応答時間を待つことはない。 画面はプラットフォーム自身の速さで応答し、結果は用意でき次第表示されます。
よくある質問
遅い API プロバイダーが、なぜプラットフォーム全体を遅くするのですか?
プラットフォームの worker がそのプロバイダーを待っているからです。同期呼び出しはプロバイダーが応答するまで worker を一つ占有し、その worker はほかのすべての機能と共有されています。一つのプロバイダーが遅くなると worker が尽き、そのプロバイダーとは関係のないリクエストまで後ろに並びます。
WebSocket が送金 API の代わりになるのですか?
いいえ。送金そのものは冪等キー付きの HTTP リクエストのままで、双方の台帳に記録されます。WebSocket が置き換えるのは待つ部分、つまり占有された接続、状態のポーリング、コールバックです。結果はイベントとして届きます。
送金が完了したときにプラットフォームが切断されていたら?
何も失われません。双方の台帳にすでに送金が記録されているからです。pusher-js などのクライアントライブラリは自動で再接続し、処理中の送金をまとめて確認すれば取りこぼしも処理されます。適用済みのイベントは送金 ID で無視します。
接続はいくつ必要ですか?
プラットフォームのバックエンドから各プロバイダーのチャンネルへ WebSocket 接続が一つあれば、そのプロバイダーとのすべての送金をまかなえます。ユーザー数は関係ありません。Hibira では 1 回の発行が 1 メッセージで、有料プランでは購読者への配信を数えません。料金をご覧ください。
製品名は各所有者の商標です。