Performance-Optimierung
Diese Seite beschreibt Strategien, um die Performance von LowCode-Flows zu verbessern. Von der Identifikation von Engpaessen bis hin zu konkreten Optimierungstechniken.
Engpaesse identifizieren
Zeitstempel-Methode
Fuegen Sie an kritischen Stellen Zeitstempel hinzu, um langsame Abschnitte zu finden:
// Function Node: Zeitmessung starten
msg._startTime = Date.now();
return msg;// Function Node: Zeitmessung auswerten
const elapsed = Date.now() - msg._startTime;
if (elapsed > 1000) {
node.warn(`Langsame Ausfuehrung: ${elapsed}ms`);
}
msg._executionTime = elapsed;
return msg;Node-RED Status nutzen
Zeigen Sie die Ausfuehrungszeit direkt im Editor an:
// Function Node: Status mit Ausfuehrungszeit setzen
const start = Date.now();
// ... Verarbeitung ...
msg.payload = await processData(msg.payload);
const elapsed = Date.now() - start;
node.status({
fill: elapsed > 500 ? "red" : "green",
shape: "dot",
text: `${elapsed}ms - ${new Date().toLocaleTimeString()}`
});
return msg;Async vs. Sync in Function Nodes
Synchron (blockierend)
Synchroner Code blockiert den gesamten Node-RED-Prozess:
// SCHLECHT: Blockiert den Event-Loop
const data = heavyComputation(msg.payload); // 2 Sekunden blockiert
msg.payload = data;
return msg;Asynchron (nicht-blockierend)
Asynchroner Code laesst andere Messages parallel verarbeiten:
// GUT: Non-blocking mit async/await
const response = await fetch("https://api.example.com/data");
msg.payload = await response.json();
return msg;Schwere Berechnungen aufteilen
Wenn rechenintensive Operationen unvermeidbar sind, teilen Sie diese auf:
// Function Node: Daten in Chunks verarbeiten
const items = msg.payload;
const chunkSize = 100;
const results = [];
for (let i = 0; i < items.length; i += chunkSize) {
const chunk = items.slice(i, i + chunkSize);
const processed = chunk.map(item => transformItem(item));
results.push(...processed);
// Event-Loop zwischen Chunks freigeben
if (i + chunkSize < items.length) {
await new Promise(resolve => setImmediate(resolve));
}
}
msg.payload = results;
return msg;Caching-Strategien mit Context
Einfacher Cache im Flow-Context
// Function Node: Daten aus Cache oder API laden
const cacheKey = "userList";
const cacheTTL = 60000; // 60 Sekunden
const cached = flow.get(cacheKey);
if (cached && (Date.now() - cached.timestamp) < cacheTTL) {
// Aus dem Cache laden
msg.payload = cached.data;
msg._fromCache = true;
return msg;
}
// Frisch laden
const response = await fetch(env.get("API_URL") + "/users");
const data = await response.json();
// Im Cache speichern
flow.set(cacheKey, {
data: data,
timestamp: Date.now()
});
msg.payload = data;
msg._fromCache = false;
return msg;Cache invalidieren
// Function Node: Cache bei Aenderungen leeren
flow.set("userList", null);
node.warn("Cache invalidiert: userList");
return msg;Globaler Cache fuer tabuebergreifende Daten
// Function Node: Konfiguration im globalen Cache halten
const config = global.get("appConfig");
if (!config) {
// Beim ersten Aufruf laden und global cachen
const response = await fetch(env.get("CONFIG_URL"));
const data = await response.json();
global.set("appConfig", data);
msg.payload = data;
} else {
msg.payload = config;
}
return msg;Rate Limiting mit Delay Node
Delay Node konfigurieren
Der Delay Node kann die Nachrichtenrate begrenzen:
| Einstellung | Wert | Beschreibung |
|---|---|---|
| Action | Rate Limit | Nachrichten drosseln |
| Rate | 10 msg/s | Maximal 10 Nachrichten pro Sekunde |
| Drop intermediate | Ja | Ueberschuessige Nachrichten verwerfen |
Manuelles Rate Limiting
// Function Node: Rate Limiting mit Zeitstempel
const rateLimitMs = 1000; // Mindestens 1 Sekunde zwischen Aufrufen
const lastCall = flow.get("lastApiCall") || 0;
const now = Date.now();
if (now - lastCall < rateLimitMs) {
node.warn("Rate Limit erreicht, Nachricht verworfen");
return null; // Nachricht nicht weitergeben
}
flow.set("lastApiCall", now);
return msg;Message Queuing
Einfache Warteschlange mit Context
// Function Node: Nachricht in Warteschlange einreihen
let queue = flow.get("messageQueue") || [];
queue.push({
payload: msg.payload,
timestamp: Date.now()
});
flow.set("messageQueue", queue);
node.status({
fill: "blue",
shape: "ring",
text: `${queue.length} in Warteschlange`
});
return null; // Nachricht nicht sofort weiterleiten// Function Node: Warteschlange abarbeiten (mit Inject Node im Intervall)
let queue = flow.get("messageQueue") || [];
if (queue.length === 0) {
return null;
}
const item = queue.shift();
flow.set("messageQueue", queue);
msg.payload = item.payload;
node.status({
fill: "green",
shape: "dot",
text: `${queue.length} verbleibend`
});
return msg;Allgemeine Optimierungstipps
| Tipp | Beschreibung |
|---|---|
| JSON.parse vermeiden | Nutzen Sie msg.payload direkt, wenn es bereits ein Objekt ist |
| Payload-Groesse reduzieren | Entfernen Sie unbenoetigte Properties mit delete msg.largeData |
| Debug Nodes deaktivieren | Deaktivierte Debug Nodes haben keinen Overhead |
| Subflows sparsam nutzen | Jeder Subflow-Aufruf erzeugt einen kleinen Overhead |
| HTTP Keep-Alive nutzen | Verwenden Sie persistente Verbindungen bei haeufigen API-Aufrufen |
| Context-Store waehlen | Nutzen Sie memory fuer temporaere und file nur fuer persistente Daten |
Speicherverbrauch reduzieren
// Function Node: Grosse Payloads frueh reduzieren
// Nur benoetigte Felder behalten
msg.payload = msg.payload.map(item => ({
id: item.id,
name: item.name,
status: item.status
// Alle anderen Felder werden nicht weitergeleitet
}));
return msg;Weiterführende Informationen
- Debugging-Techniken — Performance-Profiling
- Deployment — Produktivumgebung optimieren
- Troubleshooting — Performance-Probleme loesen