Notitie
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen u aan te melden of de directory te wijzigen.
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen de mappen te wijzigen.
Bij het ontwikkelen voor Azure Functions met TypeScript en Node.jsmoet je begrijpen hoe je functies presteren en hoe die prestaties invloed hebben op de schaalbaarheid van je functieapp. Dit inzicht is belangrijker bij het ontwerpen van hoogpresterende apps. De belangrijkste factoren om te overwegen bij het ontwerpen, schrijven en configureren van je functie-apps zijn horizontale schaalbaarheid, throughput-prestatieconfiguraties en Node.js-specifieke optimalisaties.
Horizontale schaalaanpassing
Azure Functions bewaakt standaard automatisch de belasting van uw toepassing en maakt zo nodig meer hostexemplaren voor Node.js. Azure Functions gebruikt ingebouwde drempels voor verschillende triggertypes om te bepalen wanneer instanties worden toegevoegd, zoals de leeftijd van berichten en de wachtrijgrootte voor QueueTrigger. Je kunt deze drempels niet configureren. Zie Gebeurtenisgestuurd schalen in Azure Functions voor meer informatie.
Om de schaalbaarheid van je Node.js functie-app te verbeteren, volg deze aanpak in volgorde:
- Begin met de standaardinstellingen en optimaliseer eerst je code door asynchroon I/O-patronen te gebruiken.
- Schaal uit (voeg meer instanties toe) voordat je het aantal werkprocessen opschaalt.
- Verhoog
FUNCTIONS_WORKER_PROCESS_COUNTalleen nadat uit belastingstests is gebleken dat de CPU op elke instantie volledig verzadigd is. - Voor I/O-gebonden workloads houd je het aantal werknemers conservatief en richt je op het verminderen van blokkerende oproepen en afhankelijkheidslatentie.
- Test na elke wijziging opnieuw en monitor doorvoer, latentie en foutpercentages om verbeteringen te valideren.
Doorvoerprestaties verbeteren
De standaardconfiguraties zijn geschikt voor de meeste Azure Functions-applicaties. U kunt echter de prestaties van de doorvoer van uw toepassingen verbeteren door configuraties te gebruiken op basis van uw workloadprofiel. De eerste stap is het begrijpen van het type workload dat u uitvoert.
| Werkbelastingtype | Kenmerken van functietoepassing | Examples |
|---|---|---|
| I/O-gebonden | • App moet veel gelijktijdige aanroepen verwerken. • App verwerkt een groot aantal I/O-gebeurtenissen, zoals netwerkoproepen en schijflees-/schrijfbewerkingen. |
• Web API's • Databasebewerkingen • Bestandsverwerking |
| CPU-gebonden | • De app voert langdurige berekeningen uit, zoals beeldverwerking. • De app voert datatransformaties of complexe berekeningen uit. |
• Gegevensverwerking • Inhoudstransformatie • Algoritme-uitvoering |
Omdat function workloads in de praktijk meestal een mix van I/O- en CPU-gebonden taken vormen, moet je de app onder realistische productiebelasting profileren. De volgende secties behandelen specifieke optimalisaties:
- Async/await-patronen
- Meerdere taalwerkprocessen
- Event loop-optimalisatie
- Geheugenbeheer
- Codeoptimalisering
Async/await-patronen
Node.js is gebouwd op een event-drive, non-blocking I/O-model dat het licht en efficiënt maakt voor I/O-gebonden operaties. Voor applicaties die een groot aantal I/O-gebeurtenissen verwerken of gebonden zijn aan I/O, kun je de prestaties aanzienlijk verbeteren door correcte async/await-patronen te gebruiken:
-
Gebruik
Promise.all()voor parallelle bewerkingen: Als je meerdere onafhankelijke asynchroonbewerkingen hebt, voer ze dan parallel uit in plaats van achtereenvolgend. - Vermijd het blokkeren van de gebeurtenislus: Gebruik geen synchrone bewerkingen in asynchrone functies.
- Gebruik streaming voor grote data: Verwerk grote datasets met streams in plaats van alles in het geheugen te laden.
- Implementeer correcte foutafhandeling: Gebruik try/catch-blokken en behandel Promise-afwijzingen.
Het volgende voorbeeld demonstreert parallelle asynchroonbewerkingen met Promise.all():
const { app } = require('@azure/functions');
app.http('optimizedHttpTrigger', {
methods: ['GET', 'POST'],
handler: async (request, context) => {
// Good: Using async/await for HTTP calls
const response = await fetch('https://api.example.com/data');
const data = await response.json();
// Good: Parallel async operations
const [user, orders] = await Promise.all([
fetchUser(data.userId),
fetchOrders(data.userId)
]);
return {
jsonBody: { user, orders }
};
}
});
async function fetchUser(userId) {
const response = await fetch(`https://api.example.com/users/${userId}`);
return await response.json();
}
async function fetchOrders(userId) {
const response = await fetch(`https://api.example.com/orders?userId=${userId}`);
return await response.json();
}
module.exports = async function (context, req) {
// Good: Using async/await for HTTP calls
const response = await fetch('https://api.example.com/data');
const data = await response.json();
// Good: Parallel async operations
const [user, orders] = await Promise.all([
fetchUser(data.userId),
fetchOrders(data.userId)
]);
context.res = {
body: { user, orders }
};
};
async function fetchUser(userId) {
const response = await fetch(`https://api.example.com/users/${userId}`);
return await response.json();
}
async function fetchOrders(userId) {
const response = await fetch(`https://api.example.com/orders?userId=${userId}`);
return await response.json();
}
Werkprocessen met meerdere talen gebruiken
Standaard heeft elk hostexemplaar van Functions één taalwerkerproces. U kunt het aantal werkprocessen per host (maximaal 10) verhogen met behulp van de FUNCTIONS_WORKER_PROCESS_COUNT toepassingsinstelling. Azure Functions probeert vervolgens gelijktijdige functieaanroepen over deze werknemers gelijkmatig te verdelen.
Warning
Gebruik de FUNCTIONS_WORKER_PROCESS_COUNT instelling voorzichtig. Meerdere processen die in hetzelfde exemplaar worden uitgevoerd, kunnen leiden tot onvoorspelbaar gedrag en kunnen de laadtijden van de functie verhogen. Als je deze instelling gebruikt, kan het draaien vanuit een pakketbestand deze nadelen compenseren.
Voor CPU-gebonden apps stel je het aantal taalarbeiders in op gelijk of hoger dan het aantal cores dat per functie-app beschikbaar is. Zie Beschikbare exemplaar-SKU's voor meer informatie.
I/O-gebonden applicaties kunnen er ook baat bij hebben het aantal workerprocessen te verhogen tot boven het aantal beschikbare cores. Houd er rekening mee dat het instellen van het aantal werknemers dat te hoog is, invloed kan hebben op de algehele prestaties vanwege het toegenomen aantal vereiste contextswitches.
Optimalisatie van de event loop
Node.js gebruikt een single-threaded eventloop om I/O-bewerkingen te verwerken. Het optimaliseren van hoe je code met de event loop interacteert is cruciaal voor de prestaties.
Houd de event loop vrij
Synchrone lussen die zware berekeningen uitvoeren, blokkeren de gebeurtenislus en voorkomen dat andere aanroepen worden verwerkt. Breek in plaats daarvan grote datasets op in batches en geef controle tussen batches met setImmediate:
async function processDataNonBlocking(data: any[]) {
const batchSize = 100;
for (let i = 0; i < data.length; i += batchSize) {
const batch = data.slice(i, i + batchSize);
await Promise.all(
batch.map(item => processItem(item))
);
// Allow event loop to process other tasks
await new Promise(resolve => setImmediate(resolve));
}
}
Monitor gebeurtenislusvertraging
Gebruik het volgende patroon om te meten hoe lang taken wachten in de event loop queue:
import { performance } from 'perf_hooks';
function measureEventLoopDelay(context: any) {
const start = performance.now();
setImmediate(() => {
const delay = performance.now() - start;
context.log(`Event loop delay: ${delay}ms`);
});
}
Geheugenbeheer
Efficiënt geheugengebruik is cruciaal voor de prestaties van Azure Functions, vooral wat betreft cold starts en scaling. Volg deze algemene richtlijnen bij het optimaliseren van je geheugengebruik:
- Minimaliseer globale variabelen: Zware objecten binnen het globale bereik beïnvloeden koude starttijden.
- Gebruik verbindingspooling: Hergebruik databaseverbindingen en HTTP-clients.
- Ruim de middelen op: sluit de verbindingen goed en maak timers vrij.
import { app, InvocationContext, Timer } from '@azure/functions';
// Good: Connection pooling
class DatabaseManager {
private static instance: DatabaseManager;
private connectionPool: any;
private constructor() {
this.connectionPool = createConnectionPool({
max: 10,
min: 2,
idleTimeoutMillis: 30000
});
}
public static getInstance(): DatabaseManager {
if (!DatabaseManager.instance) {
DatabaseManager.instance = new DatabaseManager();
}
return DatabaseManager.instance;
}
public async query(sql: string): Promise<any> {
const client = await this.connectionPool.connect();
try {
return await client.query(sql);
} finally {
client.release();
}
}
}
async function timerTrigger(myTimer: Timer, context: InvocationContext): Promise<void> {
const db = DatabaseManager.getInstance();
const results = await db.query('SELECT * FROM users');
context.log(`Processed ${results.length} users`);
}
app.timer('timerTrigger', {
schedule: '0 */5 * * * *',
handler: timerTrigger
});
Code-optimalisatie
Je kunt je code verder optimaliseren door je app te bundelen, een luie laadstrategie te gebruiken en je datastructuren te optimaliseren.
Bundel je afhankelijkheden
Gebruik bundeltools zoals esbuild, webpack of rollup om je functie-app in minder bestanden te combineren. Bundelen vermindert het aantal bestanden dat de runtime laadt tijdens cold start, wat direct de opstarttijd verbetert, vooral voor apps met grote afhankelijkheidsbomen. Voor meer informatie, zie Bundel vóór uitrol.
Minimaliseer de laadtijd van modules
Het laden van zware modules op de globale scope verhoogt de cold start-tijd omdat elke import vóór de eerste aanroep loopt. Gebruik dynamische imports om de belasting uit te stellen totdat de module daadwerkelijk nodig is:
async function processData(data: any) {
const { heavyFunction } = await import('heavy-library');
return heavyFunction(data);
}
Gebruik efficiënte datastructuren
Als je vaak items moet opzoeken, gebruik dan een Map in plaats van Array.find(). Een Map biedt O(1)-lookups vergeleken met O(n) voor het doorzoeken van een array:
const userMap = new Map();
const users = await fetchUsers();
users.forEach(user => userMap.set(user.id, user));
const user = userMap.get(userId); // O(1) lookup
Bewaking en profilering
Gebruik context.log() deze om de uitvoeringstijd binnen je functiehandler bij te houden. Voor aangepaste metrics en afhankelijkheidstracking gebruik je direct de Application Insights Node.js SDK . Raadpleeg Aangepaste gegevens bijhouden voor meer informatie.
Het volgende voorbeeld logt de uitvoeringsduur en het geheugengebruik:
import { app, HttpRequest, HttpResponseInit, InvocationContext } from '@azure/functions';
async function trackedFunction(request: HttpRequest, context: InvocationContext): Promise<HttpResponseInit> {
const startTime = Date.now();
const memoryBefore = process.memoryUsage().heapUsed;
try {
const result = await processData(request);
const duration = Date.now() - startTime;
const memoryDelta = (process.memoryUsage().heapUsed - memoryBefore) / 1024 / 1024;
context.log(`Execution time: ${duration}ms, Memory delta: ${memoryDelta.toFixed(2)}MB`);
return { jsonBody: result };
} catch (error) {
context.error(`Function failed after ${Date.now() - startTime}ms`, error);
throw error;
}
}
app.http('trackedFunction', {
methods: ['GET', 'POST'],
handler: trackedFunction
});
Door deze aanbevelingen op te volgen, kun je de prestaties en schaalbaarheid van je Node.js Azure Functions applicaties aanzienlijk verbeteren.