Yoklama tabanlı bir web uygulamasının sınırlamalarını analiz etme

Tamamlandı

Yoklama tabanlı web uygulaması.

Uygulamanın geçerli mimarisi, bir zamanlayıcı temelinde sunucudan tüm hisse senedi fiyat bilgilerini getirerek hisse senedi bilgilerini raporlar. Bu tasarıma genellikle yoklama temelli tasarım da denir.

Sunucu

Hisse senedi fiyatı bilgileri bir Azure Cosmos DB veritabanında depolanır. BIR HTTP isteği tarafından tetiklendiğinde işlev getStocks veritabanındaki tüm satırları döndürür.

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,
        };
    },
});
  • Veri alma: Kodun ilk bölümü olan cosmosInput, Cosmos DB'deki stocks veritabanında sorgusuyla SELECT * from cbirlikte tablodaki stocksdb tüm öğeleri alır.
  • Veri döndürme: Kodun ikinci bölümü olan app.http, bu verileri işlevine giriş context.extraInputs olarak alır ve ardından yanıt gövdesi olarak istemciye geri döndürür.

İstemci

Örnek istemci, kullanıcı arabirimini oluşturmak için Vue.js ve API'ye yönelik istekleri işlemek için Fetch istemcisini kullanır.

HTML sayfası, hisse senedi istemek için sunucuya beş saniyede bir istek göndermek için bir zamanlayıcı kullanır. Yanıt, bir hisse senedi dizisi döndürür ve bunlar kullanıcıya gösterilir.

<!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 Yöntem yoklama işlemine başladıktan sonra beş update saniyede bir çağrılır. yönteminin update içinde API uç noktasına bir GET isteği gönderilir /api/getStocks ve sonuç olarak ayarlanır app.stocksve bu da kullanıcı arabirimini güncelleştirir.

Sunucu ve istemci kodu nispeten basittir: tüm verileri alın, tüm verileri görüntüleyin. Analizimizde bulduğumuz gibi bu basitlik bazı sınırlamaları da beraberinde getiriyor.

Prototip çözümün analizi

Tailwind Traders mühendisi olarak bu zamanlayıcı tabanlı yoklama yaklaşımının bazı dezavantajlarını belirlediniz.

  • Gereksiz API istekleri: Zamanlayıcı tabanlı yoklama prototipinde istemci uygulaması, temel alınan verilerde değişiklik yapılıp yapılmadığı konusunda sunucuyla iletişim kurar.

  • Gereksiz sayfa yenilemeleri: Veriler sunucudan döndürüldükten sonra, hiçbir veri değişmemiş olsa bile web sayfasında hisse senetleri listesinin tamamı güncelleştirilir. Bu yoklama mekanizması verimsiz bir çözümdür.

  • Yoklama aralıkları: Senaryonuz için en iyi yoklama aralığını seçmek de zor bir görevdir. Yoklama, sizi arka uca yapılan her çağrının maliyetinin ne kadar olacağı ve uygulamanızın yeni verilere ne kadar hızlı yanıt vereceği arasında bir seçim yapmaya zorlar. Yeni verilerin kullanılabilir duruma gelmesiyle uygulamanın bunu algılama süresi arasında genellikle gecikmeler olur. Aşağıdaki çizimde bu sorun gösterilir.

    Her beş dakikada bir zaman çizelgesini ve yoklama tetikleyicisi denetimini gösteren çizim. Yeni veriler yedi dakika sonra kullanılabilir hale gelir. Uygulama, 10 dakikada gerçekleşen bir sonraki ankete kadar yeni verilerin farkında değildir.

    En kötü durumda, yeni verilerin algılanmasındaki olası gecikme yoklama aralığına eşit olur. Peki neden daha küçük bir aralık kullanılmıyor?

  • Veri miktarı: Uygulama ölçeklendirildikçe, istemci ile sunucu arasında değiştirilen veri miktarı sorun haline gelir. Her HTTP istek üst bilgisi, oturumun tanımlama bilgisinin yanı sıra yüzlerce baytlık veri içerir. Tüm bu ek yük, kaynakların özellikle sunucu ağır yük altındayken boşa tüketilmesine neden olur ve sunucuya gereksiz yük bindirir.

Artık prototipi daha iyi bildiğinize göre, artık uygulamayı makinenizde çalıştırmanın zamanı geldi.

CORS’yi destekleme

İşlevler Uygulamasının local.settings.json dosyasında, Host bölümü aşağıdaki ayarları içerir.

{
  "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
  }
}

Bu yapılandırma, localhost:3000 konumunda çalışan bir web uygulamasının localhost:7071 adresinde çalışan işlev uygulamasına istekte bulunmasını sağlar. özelliği CORSCredentials , işlev uygulamasına istekten kimlik bilgisi tanımlama bilgilerini kabul etmelerini söyler.

Çıkış noktaları arası kaynak paylaşımı (CORS), bir etki alanında çalışan web uygulamasının başka bir etki alanındaki kaynaklara erişmesini sağlayan bir HTTP özelliğidir. Web tarayıcıları, bir web sayfasının API'leri farklı etki alanından çağırmasını engelleyen, aynı çıkış noktası ilkesi olarak bilinen bir güvenlik kısıtlaması uygular. CORS, bir etki alanına (çıkış noktası etki alanı) başka etki alanındaki API'leri çağırmasına izin veren güvenli bir yol sağlar.

Yerel olarak çalışırken, CORS sizin için hiçbir zaman yayımlanmayan örneğin local.settings.json dosyasında yapılandırılır. İstemci uygulamasını (birim 7) dağıttığınızda, istemci uygulamasından erişime izin vermek için işlev uygulamasındaki CORS ayarlarını da güncelleştirmeniz gerekir.