ポーリング ベースの Web アプリの制限事項を分析する
アプリケーションの現在のアーキテクチャでは、タイマーに基づいてサーバーから すべての 株価情報をフェッチすることで、株式情報を報告します。 この設計は、多くの場合、ポーリング ベースの設計と呼ばれます。
サーバー
株価情報は、Azure Cosmos DB データベースに格納されます。 HTTP 要求によってトリガーされると、関数 getStocks
はデータベースからすべての行を返します。
import { app, input } from "@azure/functions";
const cosmosInput = input.cosmosDB({
databaseName: 'stocksdb',
containerName: 'stocks',
connection: 'COSMOSDB_CONNECTION_STRING',
sqlQuery: 'SELECT * from c',
});
app.http('getStocks', {
methods: ['GET'],
authLevel: 'anonymous',
extraInputs: [cosmosInput],
handler: (request, context) => {
const stocks = context.extraInputs.get(cosmosInput);
return {
jsonBody: stocks,
};
},
});
-
データの取得: コードの最初のセクション cosmosInput は、cosmos DB の
stocks
データベース内のクエリSELECT * from c
を使用して、stocksdb
テーブル内のすべての項目を取得します。 -
データの返し: コードの 2 番目のセクション app.http は、そのデータを
context.extraInputs
の入力として関数に受け取り、応答本文としてクライアントに返します。
顧客
サンプル クライアントでは、Vue.js を使用して UI を作成し、Fetch クライアントを使用して API への要求を処理します。
HTML ページでは、タイマーを使用して 5 秒ごとにサーバーに要求を送信し、株式を要求します。 応答は株式の配列を返し、その後ユーザーに表示されます。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/bulma/0.7.4/css/bulma.min.css" integrity="sha256-8B1OaG0zT7uYA572S2xOxWACq9NXYPQ+U5kHPV1bJN4=" crossorigin="anonymous" />
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.8.1/css/all.css" integrity="sha384-50oBUHEmvpQ+1lW4y57PTFmhCaXp0ML5d60M1M7uH2+nqUivzIebhndOJK28anvf" crossorigin="anonymous">
<link rel="stylesheet" href="style.css">
<title>Stocks | Enable automatic updates in a web application using Azure Functions and SignalR</title>
</head>
<body>
<!-- BEGIN: Replace markup in this section -->
<div id="app" class="container">
<h1 class="title">Stocks</h1>
<div id="stocks">
<div v-for="stock in stocks" class="stock">
<div class="lead">{{ stock.symbol }}: ${{ stock.price }}</div>
<div class="change">Change:
<span :class="{ 'is-up': stock.changeDirection === '+', 'is-down': stock.changeDirection === '-' }">
{{ stock.changeDirection }}{{ stock.change }}
</span></div>
</div>
</div>
</div>
<!-- END -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.6.10/vue.min.js" integrity="sha256-chlNFSVx3TdcQ2Xlw7SvnbLAavAQLO0Y/LBiWX04viY=" crossorigin="anonymous"></script>
<script src="bundle.js" type="text/javascript"></script>
</body>
</html>
import './style.css';
function getApiUrl() {
const backend = process.env.BACKEND_URL;
const url = (backend) ? `${backend}` : ``;
return url;
}
const app = new Vue({
el: '#app',
interval: null,
data() {
return {
stocks: []
}
},
methods: {
async update() {
try {
const url = `${getApiUrl()}/api/getStocks`;
console.log('Fetching stocks from ', url);
const response = await fetch(url);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
app.stocks = await response.json();
} catch (ex) {
console.error(ex);
}
},
startPoll() {
this.interval = setInterval(this.update, 5000);
}
},
created() {
this.update();
this.startPoll();
}
});
startPoll
メソッドがポーリングを開始すると、update
メソッドが 5 秒ごとに呼び出されます。
update
メソッド内では、GET 要求が /api/getStocks
API エンドポイントに送信され、結果が app.stocks
に設定され、UI が更新されます。
サーバーとクライアントのコードは比較的簡単です。すべてのデータを取得し、すべてのデータを表示します。 分析でわかるように、このシンプルさにはいくつかの制限があります。
プロトタイプ ソリューションの分析
Tailwind Traders のエンジニアとして、このタイマー ベースのポーリング アプローチの欠点をいくつか特定しました。
不要な API 要求: タイマー ベースのポーリング プロトタイプでは、クライアント アプリケーションは、基になるデータに対する変更が存在するかどうかに関係なく、サーバーに接続します。
不要なページの更新: サーバーからデータが返されると、データが変更されなくても、株式のリスト全体が Web ページで更新されます。 このポーリング メカニズムは非効率的なソリューションです。
ポーリング間隔: シナリオに最適なポーリング間隔を選択することも課題です。 ポーリングでは、バックエンドへの各呼び出しのコストと、アプリが新しいデータに応答する速度を選択する必要があります。 多くの場合、新しいデータが使用可能になるまで、アプリで検出されるまでの遅延が発生します。 次の図は、この問題を示しています。
最悪の場合、新しいデータを検出するための潜在的な遅延はポーリング間隔と等しくなります。 それにもかかわらず、なぜより短い間隔を使用しないのでしょうか。
データの量: アプリケーションがスケーリングするにつれて、クライアントとサーバーの間で交換されるデータの量が問題になります。 各 HTTP 要求ヘッダーには、セッションの Cookie と共に数百バイトのデータが含まれています。 このオーバーヘッドはすべて、特に負荷が高い場合は、無駄なリソースを作成し、サーバーに不必要に課税します。
プロトタイプについて詳しく理解したら、次はマシンでアプリケーションを実行します。
CORS のサポート
Functions アプリの local.settings.json ファイルの Host
セクションには、次の設定が含まれています。
{
"IsEncrypted": false,
"Values": {
"AzureWebJobsStorage": "<STORAGE_CONNECTION_STRING>",
"FUNCTIONS_WORKER_RUNTIME": "node",
"AzureWebJobsFeatureFlags": "EnableWorkerIndexing",
"COSMOSDB_CONNECTION_STRING": "<COSMOSDB_CONNECTION_STRING>"
},
"Host" : {
"LocalHttpPort": 7071,
"CORS": "http://localhost:3000",
"CORSCredentials": true
}
}
この構成により、 localhost:3000 で実行されている Web アプリケーションは 、localhost:7071 で実行されている関数アプリに要求を行うことができます。 プロパティ CORSCredentials
は、要求から資格情報 Cookie を受け入れるように関数アプリに指示します。
クロスオリジン リソース共有 (CORS) は、あるドメインで実行されている Web アプリケーションが別のドメインのリソースにアクセスできるようにする HTTP 機能です。 Web ブラウザーは、Web ページが別のドメインの API を呼び出さないようにする、同じ配信元ポリシーと呼ばれるセキュリティ制限を実装します。CORS は、あるドメイン (配信元ドメイン) が別のドメインの API を呼び出せるようにする安全な方法を提供します。
ローカルで実行すると、CORS はサンプルの local.settings.json ファイルで構成されます。これは発行されません。 クライアント アプリ (ユニット 7) を展開する場合は、関数アプリの CORS 設定も更新して、クライアント アプリからのアクセスを許可する必要があります。