見出し画像

大規模データ送信とpolling処理を行う処理でAzure FrontDoorを経由した時のみリクエストが失敗した話


はじめに

Acompanyプロダクト部門の高橋です。本記事は「#バレンタインアドカレ」17日目の記事となります。

最近社内でClient-ServerモデルシステムのSSLオフロードとしてAzure Front Doorを使っていました。
その際にエラーでハマってしまったため備考録として記載しようと思います。

Azure FrontDoorとは?

公式ドキュメントによると以下のようにまとまっています。

Azure Front Door は、Microsoft の最新のクラウド コンテンツ配信ネットワーク (CDN) であり、世界中のユーザーとアプリケーションの静的および動的 Web コンテンツの間で、高速で信頼性が高く、セキュリティで保護されたアクセスを提供します。 Azure Front Door は、Microsoft のグローバル エッジ ネットワークを使用してコンテンツを配信します。このネットワークでは、数百のグローバルおよびローカルのポイント オブ プレゼンス (POP) が、企業とコンシューマー エンド ユーザー双方に近い世界各地の場所に分散配置されています。

https://learn.microsoft.com/ja-jp/azure/frontdoor/front-door-overview

また、マネージドのドメインが発行され、そのドメイン経由でアクセスすることでSSL通信を行うことができます。Azure側でSSL証明書が管理されているので、ユーザが管理しなくて良いのは嬉しい点ですね。

発生した問題

以下の二つの問題が発生しました。

  1. 複数回リクエストを実行するとサーバが応答しなくなる

  2. 大きなデータでリクエストを行うとクライアント側がサイレントで落ちる

それぞれについての説明と原因、対処法について記載します。

 複数回リクエストを実行するとサーバが応答しなくなる

説明

今回のClient-Serverの構成ではClientからServerに計算リクエストを送ったあと、Serverが計算を完了したかをClientから結果取得リクエストを送るようにしていました。
この結果取得リクエストを3秒ごとに叩き続けると、2分ほどでサーバーがリクエストを受け付けないようになりエラーが返されるようになりました。

原因

結論から書くとバックエンドのサーバ側で設定したkeepaliveのタイムアウトが1000秒と過剰すぎたことが原因でした。
先述した結果取得リクエストではリクエストを実行するたびに新しいコネクションを生やしており、裏側で使用しているLoadBalancerのコネクションの上限に達成してしまったと考えられます。

対処法

keepaliveのタイムアウト設定を5秒に変更することで解決しました。

大きなデータでリクエストを行うとクライアント側がサイレントで落ちる

説明

ClientからServerへ6GBほどのCSVファイルを送っている最中にClient側がログを吐かずに停止し、exit codeを確認すると141(SIGPIPE)でした。

SIGPIPEで検索をすると以下のサイトが出てきました。

どうやらコネクションが切れたソケットに書き込みを行ってしまったためエラーになったようです。

原因

(おそらくですが)Azure Front Doorのアップロードのサイズ制限に引っかかったことが原因でした。
公式のサイトを確認するとアップロードは2GBまでのようでした。

対処法

データの送信方法を一括送信から約10MBごとにchunkに分割して送信するようにしたところ解決しました。

以下、修正内容です。
※ 本システムではClientとServerの通信部分にcpp-httplibを使っております。

修正前

auto res = client.Post("/request", request_json, "application/json");

修正後

auto res = client.Post(
        "/request",
        [&request_json](size_t offset, DataSink &sink)
        {
                        // 1e7文字(1文字1byteで1e7byte=約10MB)ごとに分割する。
            size_t chunk_size = 1e7;
            for (size_t i = 0; i < request_json.size(); i += chunk_size)
            {
                size_t data_size = std::min(chunk_size, request_json.size() - i);
                sink.os.write(request_json.data() + i, data_size);
            }
            sink.done();
            return true;
        },
        "application/json"
    );

最後に宣伝

Acompanyはプライバシー保護とデータ活用の両立を追求するデータクリーンルームをベースに、次なるデータ市場を拓くプラットフォームを展開しています!

Acompanyでは一緒に困難を乗り越えてくれる仲間を募集しています!


この記事が気に入ったらサポートをしてみませんか?