ハンドシェイク税
Magentoの連携が遅い原因は、隠れたネットワークコストにあるかもしれません。
かつて、価格APIと通信する商品エクスポートを実行したことがあります。1つの商品ならすぐに終わりましたが、カタログ全体のエクスポートには膨大な時間がかかりました。データベースはアイドル状態でした。プロファイラで調べたところ、問題はネットワークにあることがわかりました。
コードの中で、ループの内部で新しいHTTPクライアントを作成していたのです。
HTTPSでデータを送信する前に、マシンは重い処理を行います。ソケットを開くためにTCPハンドシェイクを行い、次に証明書を交換して鍵を交渉するためにTLSハンドシェイクを行います。これには数回のラウンドトリップが必要です。
これを一度だけ行うならコストは低いですが、40,000個の商品を扱うループ内で行うと、そのコストを40,000回支払うことになります。実際のデータ量はわずかですが、セットアップ部分が非常に高価なのです。
PHPでは、クライアントを作成してすぐに破棄してもよいように感じることがあります。これは単一のWebリクエストであれば機能しますが、長時間実行されるプロセスでは失敗します。
cronジョブ、コンソールコマンド、またはメッセージキューのコンシューマーでは、以下のパターンを避けてください:
- foreach ($products as $product) {
- $client = new \GuzzleHttp\Client();
- $client->post('https://api.example.com/sync', [...]);
- }
このコードは、商品ごとに新しい接続を開き、完全なハンドシェイクを実行します。
Guzzleは、同じクライアントインスタンスを使用していれば、接続を維持(Keep-Alive)します。クライアントをループの外に移動してください:
- $client = new \GuzzleHttp\Client(['base_uri' => 'https://api.example.com']);
- foreach ($products as $product) {
- $client->post('/sync', [...]);
- }
これにより、ソケットとTLSセッションが維持されます。ハンドシェイクは一度だけで済み、あとはデータをストリームとして流すだけです。Magentoでは、クライアントを手動で作成するのではなく、コンストラクタを通じて設定済みのクライアントを注入(inject)するようにしてください。
これを行わないと、単なるレイテンシ以上の問題が発生します。送信ポートが枯渇する可能性があります。切断された接続が、OSが回収するよりも速いスピードでTIME_WAIT状態として蓄積されていきます。その結果、サービスが新しいソケットを一切開けなくなることもあります。
コードにこの間違いがないか確認してください。ターミナルで以下のコマンドを実行します:
grep -rn "new .*Client(" app/code | grep -i http
ループ内で新しいクライアントを作成している箇所がないか探してください。クライアントをループの外に出しましょう。これはわずか1行の変更ですが、大規模な同期処理において劇的な高速化をもたらします。
出典: https://dev.to/iamrobindhiman/the-handshake-tax-reuse-your-http-client-in-magento-integrations-3kk7
