Atomiska räknare och köer
Bygg tillförlitliga atomiska räknare och implementera meddelandeköer med Realtime Database för robust applikationslogik
Atomiska räknare och köer är en gratis lektion i Applikationer med Firebase Auth och Realtime Database på CoddyKit. Detta är lektion 3 av 4. Du kan läsa vilka 3 lektioner som helst i den här lärvägen kostnadsfritt i sin helhet – därefter låser CoddyKit PRO upp alla lektioner, plus praktisk övning med en inbyggd kodredigerare och en AI-lärare dygnet runt. Den ingår i lärvägen för Applikationer med Firebase Auth och Realtime Database, och Era framsteg synkroniseras mellan webben och CoddyKit-appen. Kursen i Applikationer med Firebase Auth och Realtime Database innehåller totalt 4 lektioner.
Varför atomiska operationer är viktiga
I realtidsprogram kan flera användare försöka uppdatera samma data samtidigt. Detta kan leda till det som kallas ett kapplöpningstillstånd.
- Föreställ dig att två användare gillar ett inlägg exakt samtidigt.
- Utan korrekt hantering kan den ena ”gilla”-markeringen skriva över den andra.
- Detta leder till felaktiga data, till exempel att ett inlägg visar 10 gilla-markeringar när det borde visa 11.
Atomiska operationer säkerställer att datauppdateringar utförs som en enda, odelbar enhet och förhindrar sådana problem.
Förstå atomiska räknare
En atomisk räknare är ett numeriskt värde som på ett tillförlitligt sätt kan ökas eller minskas även när flera klienter försöker ändra det samtidigt.
Det är viktigt för funktioner som:
- Att räkna gilla-markeringar eller uppröster för innehåll.
- Att hålla reda på sidvisningar eller nedladdningar.
- Att hantera lagernivåer i en e-handelsapp.
Firebase Realtime Database erbjuder en kraftfull mekanism för att implementera detta på ett säkert sätt.
Implementera med transaktioner
Firebases metod transaction() är central för att skapa atomiska operationer. Den säkerställer att en uppdateringsfunktion körs på de mest aktuella data, även om andra skrivningar sker samtidigt.
- Din uppdateringsfunktion tar emot de aktuella data.
- Den returnerar det nya värde som du vill skriva.
- Firebase försöker automatiskt köra om transaktionen om data ändras under processen.
Detta garanterar att räknarens uppdateringar alltid baseras på det senaste tillståndet.
Kod: enkel atomisk räknare
Så här ökar du en räknare atomiskt med hjälp av en transaktion. Det här exemplet simulerar Firebase-transaktionens logik.
class MockRef {
constructor(value) {
this.value = value;
}
async transaction(updateFunction) {
const currentValue = this.value;
const newValue = updateFunction(currentValue);
if (newValue !== undefined) {
this.value = newValue;
console.log(`Counter updated to: ${this.value}`);
return { committed: true, snapshot: { val: () => this.value } };
}
return { committed: false };
}
val() { return this.value; }
}
async function main() {
const counterRef = new MockRef(0);
console.log("Initial count:", counterRef.val());
// Attempt to increment the counter
await counterRef.transaction(currentCount => {
return (currentCount || 0) + 1;
});
console.log("Final count (after one increment):");
console.log(counterRef.val());
}
main();Förklaring av transaktionslogiken
I metoden transaction() tar återanropsfunktionen emot currentCount. Om räknaren inte finns (null) används 0 som standard innan värdet ökas.
- Om återanropsfunktionen returnerar
undefinedavbryts transaktionen. - Om den returnerar något annat värde, till exempel det ökade antalet, sparas transaktionen.
- Firebase hanterar automatiskt nya försök om data ändras medan transaktionen körs.
Detta säkerställer att det slutliga antalet alltid är korrekt, även under hög belastning.
Introduktion till meddelandeköer
En meddelandekö är ett sätt för olika delar av en applikation (eller olika applikationer) att kommunicera asynkront. Den fungerar som en att-göra-lista för uppgifter som inte behöver behandlas omedelbart.
Viktiga fördelar är:
- Frikoppling: Avsändare behöver inte vänta på mottagare.
- Tillförlitlighet: Uppgifter lagras tills de har behandlats.
- Skalbarhet: Det går enkelt att lägga till fler arbetare som behandlar uppgifter.
Firebase Realtime Database kan fungera som en enkel och effektiv meddelandekö.
Bygga en enkel kö
För att bygga en kö med Realtime Database skapar du vanligtvis en lista med uppgifter. Nya uppgifter läggs till i listan och arbetarprocesser hämtar och behandlar dem.
- Varje uppgift är ett objekt med relevanta data (till exempel
actionochpayload). - Med
push()skapas unika nycklar i tidsordning, vilket passar perfekt för en kö. - Arbetare lyssnar efter nya objekt och behandlar de äldsta först.
Den här strukturen möjliggör robust hantering av bakgrundsuppgifter.
Kod: Lägga till i en kö
Det är enkelt att lägga till uppgifter i en kö med Firebase-metoden push(). Varje nytt objekt får en unik nyckel.
class MockDatabase {
constructor() {
this.data = {};
}
ref(path) {
return {
push: (value) => {
const key = `item_${Object.keys(this.data[path] || {}).length}_${Date.now()}`;
if (!this.data[path]) {
this.data[path] = {};
}
this.data[path][key] = value;
console.log(`Added to ${path}: ${JSON.stringify(value)}`);
return { key: key };
},
val: () => this.data[path]
};
}
}
async function main() {
const mockDb = new MockDatabase();
const queueRef = mockDb.ref("tasks");
console.log("Adding tasks to the queue...");
await queueRef.push({ action: "sendEmail", userId: "user123" });
await queueRef.push({ action: "generateReport", reportId: "rpt456" });
console.log("\nCurrent queue items:");
console.log(JSON.stringify(queueRef.val(), null, 2));
}
main();Kod: Behandla från en kö
För att behandla uppgifter på ett tillförlitligt sätt måste du säkerställa att endast en arbetare behandlar en viss uppgift. Det innebär att hämta den äldsta uppgiften och sedan ta bort den atomiskt eller markera den som "processed" med hjälp av en transaktion.
class MockDatabase {
constructor(initialData = {}) {
this.data = initialData;
}
ref(path) {
const self = this;
return {
orderByChild: (child) => ({ limitToFirst: (count) => ({ once: async (eventType) => {
if (eventType === 'value') {
const items = Object.entries(self.data[path] || {})
.map(([key, value]) => ({ key, value }))
.sort((a, b) => (a.value[child] || 0) - (b.value[child] || 0));
const result = {};
items.slice(0, count).forEach(item => { result[item.key] = item.value; });
return { val: () => result };
}
}}) }),
child: (key) => ({ transaction: async (updateFunction) => {
const currentValue = self.data[path] ? self.data[path][key] : null;
const newValue = updateFunction(currentValue);
if (newValue === null) {
delete self.data[path][key];
console.log(`Transaction removed item: ${key}`);
return { committed: true, snapshot: { val: () => null } };
} else if (newValue !== undefined) {
if (!self.data[path]) self.data[path] = {};
self.data[path][key] = newValue;
console.log(`Transaction updated item: ${key}`);
return { committed: true, snapshot: { val: () => newValue } };
}
return { committed: false, snapshot: { val: () => currentValue } };
}}),
val: () => self.data[path]
};
}
}
async function main() {
const initialTasks = {
"task_A": { action: "sendEmail", userId: "user123", timestamp: 1678888000000 },
"task_B": { action: "generateReport", reportId: "rpt456", timestamp: 1678888010000 }
};
const mockDb = new MockDatabase({ tasks: initialTasks });
const queueRef = mockDb.ref("tasks");
console.log("Initial queue items:", JSON.stringify(queueRef.val(), null, 2));
const snapshot = await queueRef.orderByChild('timestamp').limitToFirst(1).once('value');
const firstItem = snapshot.val();
if (firstItem) {
const firstKey = Object.keys(firstItem)[0];
console.log(`Attempting to process task with key ${firstKey}`);
const transactionResult = await queueRef.child(firstKey).transaction(currentData => {
return currentData ? null : undefined; // Delete if exists, abort if not
});
if (transactionResult.committed) {
console.log(`Successfully processed and removed task: ${firstKey}`);
} else {
console.log("Failed to process task (already processed or aborted).");
}
}
console.log("\nQueue items after processing:");
console.log(JSON.stringify(queueRef.val(), null, 2));
}
main();Välja mellan räknare och köer
Även om både atomiska räknare och köer använder Firebase-transaktioner löser de olika problem:
- Atomiska räknare: För enkla numeriska uppdateringar som kräver hög konsekvens (till exempel röstetal och lagersaldo).
- Meddelandeköer: För att frikoppla uppgifter, hantera bakgrundsprocesser och säkerställa tillförlitlig körning av jobb som kan behandlas senare.
Genom att förstå dessa mönster kan du bygga mer robusta och skalbara realtidsapplikationer.
Snabbtest: Atomiska operationer
Du vill öka en användares "score" i ditt spel på ett tillförlitligt sätt och säkerställa att samtidiga uppdateringar från olika enheter inte leder till att ökningar går förlorade. Vilken funktion i Firebase Realtime Database är mest lämplig?
Sammanfattning: Atomiska räknare och köer
Vi har gått igenom hur Firebase Realtime Database möjliggör robust applikationslogik genom atomiska operationer.
- Atomiska räknare använder
transaction()för att på ett tillförlitligt sätt öka eller minska numeriska värden och förhindra race conditions. - Meddelandeköer använder
push()för att lägga till uppgifter ochtransaction()för att atomiskt behandla (reservera eller ta bort) de äldsta uppgifterna. Det möjliggör asynkron och skalbar bakgrundsbehandling.
Att behärska dessa mönster är avgörande för att bygga högpresterande, konsekventa och skalbara realtidsapplikationer.
Lär dig Applikationer med Firebase Auth och Realtime Database med en AI-lärare – gratis
Skriv och kör riktig kod i webbläsaren, få omedelbar hjälp av en AI-lärare dygnet runt och fortsätt där du slutade – på webben eller i appen.
- Kurser
- 11
- Lektioner
- 44
Vanliga frågor
Är lektionen ”Atomiska räknare och köer” gratis?
Ja – du kan läsa vilka 3 lektioner som helst i lärvägen Applikationer med Firebase Auth och Realtime Database, inklusive ”Atomiska räknare och köer”, kostnadsfritt i sin helhet här på webben. Därefter låser CoddyKit PRO upp alla lektioner, plus interaktiv övning med en inbyggd kodredigerare och en AI-lärare dygnet runt. Kursen i Applikationer med Firebase Auth och Realtime Database innehåller totalt 4 lektioner.
Vad lär jag mig i ”Atomiska räknare och köer”?
Bygg tillförlitliga atomiska räknare och implementera meddelandeköer med Realtime Database för robust applikationslogik Ni övar på Applikationer med Firebase Auth och Realtime Database med praktisk kod som körs direkt i webbläsaren, medan en AI-handledare som är tillgänglig dygnet runt svarar på Era frågor under lektionen.
Behöver jag någon erfarenhet för att börja lära mig Applikationer med Firebase Auth och Realtime Database?
Du behöver inga förkunskaper. Utbildningen i Applikationer med Firebase Auth och Realtime Database på CoddyKit är upplagd för allt från nybörjare till avancerade elever, så att du kan börja här eller från början och gå fram i din egen takt. Detta är lektion 3 av 4.
Hur lång tid tar lektionen ”Atomiska räknare och köer”?
De flesta CoddyKit-lektioner tar cirka 5–10 minuter. Varje lektion är kort och interaktiv, så att du gör stadiga framsteg och kan fortsätta precis där du slutade – på webben eller i appen.
Kan jag skriva och köra kod i den här Applikationer med Firebase Auth och Realtime Database-lektionen?
Ja. Varje Applikationer med Firebase Auth och Realtime Database-lektion innehåller en inbyggd kodredigerare, så att du kan skriva och köra riktig kod direkt i webbläsaren och få omedelbar AI-feedback – utan lokal installation.
Alla lektioner i den här kursen
- Fan-out-uppdateringar av data
- Transaktionella dataoperationer
- Atomiska räknare och köer
- Strategier för denormalisering och dataduplicering