This is a viewer only at the moment see the article on how this works.
To update the preview hit Ctrl-Alt-R (or ⌘-Alt-R on Mac) or Enter to refresh. The Save icon lets you save the markdown file to disk
This is a preview from the server running through my markdig pipeline
Under Pressure: Hur kösystem hanterar baktryck med exempel i C
Sunday, 23 November 2025
"Backpressure" är den okända hjälten i distribuerade system. ., det är vad som hindrar köerna från att bli fulla när producenter skjuter upp meddelanden snabbare än kunder kan tugga igenom dem." ., enkelt uttryckt: "" :, "" det är ', systemet säger:
Här är det som händer. teknikerna i den här artikeln gäller varenda dag message queue and service bus—RabbitMQ, KafkaM SK2 Azure Service BusMSC3 AWS SQSMska4 NATSM Ska5 du namnge det Mska6 Specifikiteten varierarMka7 men de principerna är universellaMkka8 JagMske9 använder RabbitMQ för de flesta exempel eftersom det är vad jag känner till bästMcka11 men jagMssa12 ska visa hur dessa mönster överförs över plattformarMsa13
En bekännelse: Även de flesta högt uppsatta utvecklare implementerar inte rätt backpressure handling. De bygger lyckliga system som fungerar bra i dev och staging. ,. Då undrar man varför produktionen går ner under "" Black Friday "". .. Backpressure-handling är en av de tekniker som skiljer ", det fungerar, ", från ", det skalar, M SK8. Om du inte tänker på det, ,, så bygger du ett system som slutligen misslyckas under last.
Vad är baktryck
I grunden är baktrycket en feedback-loop som saktar ned producenter när konsumenterna halkar efter.1 Tänk på det som trafikljus på en skidväg.2 Du kan.3 Du kan inte bara stapla på motorvägen när du vill.4 Lamporna kontrollerar flödet.5 Att låta bilar sammanföras utan att orsaka en hög.6 Upp.7
Utan baktryck.,. En snabb producent överväldiger en långsam konsument... meddelanden staplar upp i köer, ,. minnet blir utmattat, ,. och slutligen faller ditt system över.M SK4. baktrycket säger: "" ", håll igång! "", ", innan katastrofen slår till.
flowchart LR
P[Producer] --> Q[Queue]
Q --> C[Consumer]
C -. "Slow down!" .-> P
style P stroke:#f59e0b,stroke-width:2px
style Q stroke:#0ea5e9,stroke-width:2px
style C stroke:#10b981,stroke-width:2px
skönheten med baktrycket är att det konversation mellan producent och konsument.
Hur RabbitMQ hanterar baktrycket
RabbitMQ är en open-source message broker , som implementerar Advanced Message Queuing Protocol (AMQP). Det är en av de mest utbretta messagbrokererna, ', som används av företag från nystart till företag som Bloomberg. , Pivotal , och VMware ( som nu underhåller den
Vad RabbitMQ är bäst på:
- Kompleks ruttering - Utbyter , bindningar , och spårningskyltar låter dig bygga sofistikerade meddelandenriktade topologier utbytestyper (redrigt , strömmen ut, , ämne, , opskrifte, M SK4 har du tagit itu med?
- Tillförlitlighetsgarantier - Publisher bekräftar, konsumenternas erkännande, och ständiga meddelanden innebär att du kan bygga system där meddelandets förlust är oacceptabelt.
- Flexibel användning - kör den på en enda nod för utveckling kluster med kvorum köer för hög tillgänglighet i produktion.
- Protokollstöd - Utöver AMQP MQTT (IoT), STOMP (enlig textprotocol), och har en strömmar egenskaper för Kafka
När man ska överväga alternativ Om du behöver massiv genomspetsning med en enklare routering (miljoner meddelanden /sekunderM SK2 Kafka'loggen, -baserad arkitektur kan vara mer lämpligtMSC5 Om du är allihopa på ett cloud-tillhandahållare
RabbitMQ har flera inbyggda mechanismer för att hantera baktrycket, ,, och det är viktigt att förstå dem om du designar system som behöver hålla sig upprätt under last. RabbitMQ-dokumentation är utmärkt—I'l linkar till specifika sidor medan vi går
Flow Control
När RabbitMQ's minnesmängd eller köd djup överskrider konfigurerade tröskeln, aktiverar den flödekontroll. Det här blockerar tillfälligt redaktörernas anslutningar — redaktörerna kan ' inte skicka nya meddelanden förrän دلالn har rensat tillräckligt med backlog minnesalarmer och diskalarmer som aktiverar flödetskontroll.
flowchart TD
subgraph "RabbitMQ Flow Control"
A[Publisher Sends Message] --> B{Memory/Queue<br/>Threshold OK?}
B -->|Yes| C[Message Accepted]
C --> D[Add to Queue]
B -->|No| E[Connection Blocked]
E --> F[Publisher Waits]
F --> G{Threshold<br/>Cleared?}
G -->|No| F
G -->|Yes| H[Connection Unblocked]
H --> A
end
style E stroke:#ef4444,stroke-width:3px
style H stroke:#10b981,stroke-width:2px
Den viktigaste insikten är att RabbitMQ inte bara lägger ned meddelanden under tryck, det saktar ner källan. Det här är en mycket mer civiliserad metod än att skämtvisa bort data.
Konsumenterkännelser
Konsumenterna kontrollerar hastigheten genom erkännelser ( ACKs och NACKs ). Ett meddelande är inte borttaget från köen tills konsumenten explicit erkänner det, . Om en konsument inte gör det, ' inte tar ACK- meddelanden tillräckligt snabbt, , sträcker sig köen upp, M SK6 vilket i slutändan driver flödetskontroll uppstream.
Du kan också använda förfetch gränser (, QoS, ), för att kontrollera hur många okända meddelanden en konsument kan få i flyg på samma gång.
// Set prefetch count to limit unacknowledged messages
channel.BasicQos(prefetchSize: 0, prefetchCount: 10, global: false);
Detta säger åt RabbitMQ: "Snälla skicka mig messages på samma gång . När jag har fått några,, kan du skicka fler,." Det är konsumenten som uttryckligen säger hur mycket tryck den kan hantera .NET-klientdokumentation täcker API i detalj.
Hur andra system hanterar baktryck
De mönsterna är universella, men implementeringarna skiljer sig. Här är hur några andra populära kommunikationssystem möter samma problem.
Kafka: Konsumenten-Kontrollerad val
Apache Kafka är en distribuerad event-streaming-plattform designad för hög - , genomspetsning, , , fel, - , tolerans med meddelanden och . . Till skillnad från traditionella messag-operatörer, lagrar Kafka meddelanden i en appen , - ”, bara registrerar dem och låter kunderna följa sin egen position.
Kafka tar ett helt annat tillvägagångssätt att dra meddelanden istället för att trycka på dem. Kafka Consumer-dokumentation för mer information om valmodellen.
// Kafka consumer with explicit backpressure control
using var consumer = new ConsumerBuilder<string, string>(config).Build();
consumer.Subscribe("orders");
while (!cancellationToken.IsCancellationRequested)
{
// Only fetch what you can handle - this IS your backpressure
var result = consumer.Consume(timeout: TimeSpan.FromSeconds(1));
if (result != null)
{
await ProcessMessageAsync(result.Message.Value);
// Manual commit = explicit acknowledgement
consumer.Commit(result);
}
// If processing is slow, you simply poll less frequently
// Kafka doesn't push more messages at you
}
Den smarta biten : Kafka's konsumentgrupper återbalanserar automatiskt partitionerna M SK2 Om en konsument halkar efter , kan du lägga till fler konsumenter i gruppen och partitioner blir omfördelade MSC4 baktrycket blir ett skalerande beslut
// Control batch size to manage memory pressure
var config = new ConsumerConfig
{
BootstrapServers = "localhost:9092",
GroupId = "order-processors",
AutoOffsetReset = AutoOffsetReset.Earliest,
MaxPollIntervalMs = 300000, // 5 mins max between polls
MaxPartitionFetchBytes = 1048576, // 1MB max per partition fetch
FetchMaxBytes = 52428800 // 50MB max total fetch
};
Azure Service Bus: samtida budskapkontroll
Azure Service Bus är Microsofts fullkomligt skötta företags meddelandesmakare med queues och publiceringar.
Azure Service Bus använder MaxConcurrentCalls stället på att' är vackert enkelt — det kontrollerar hur många meddelanden din processor hanterar samtidigt att processa meddelanden täcker prestationen i detalj.
var processor = client.CreateProcessor("orders-queue", new ServiceBusProcessorOptions
{
// This IS your backpressure - only process 10 at a time
MaxConcurrentCalls = 10,
AutoCompleteMessages = false,
PrefetchCount = 20 // Buffer 20 messages locally
});
processor.ProcessMessageAsync += async args =>
{
try
{
await ProcessOrderAsync(args.Message.Body.ToString());
await args.CompleteMessageAsync(args.Message);
}
catch (Exception ex)
{
// Abandon returns message to queue for retry
await args.AbandonMessageAsync(args.Message);
}
};
processor.ProcessErrorAsync += args =>
{
Console.WriteLine($"Error: {args.Exception.Message}");
return Task.CompletedTask;
};
await processor.StartProcessingAsync();
Azure Service Bus stödjer också -sessioner för Auftragsbearbeitning och död- bokstäver köer för meddelanden som misslyckas upprepade gånger
AWS SQS: Visibility Timeout Dance
Amazon Simple Queue Service (SQS) är ett fullt skött meddelande queuing service från AWS.
SQS använder syntydsperrningar som dess baktryckmekanism.
var sqsClient = new AmazonSQSClient();
// Receive with explicit backpressure control
var response = await sqsClient.ReceiveMessageAsync(new ReceiveMessageRequest
{
QueueUrl = queueUrl,
MaxNumberOfMessages = 10, // Batch size = backpressure control
WaitTimeSeconds = 20, // Long polling
VisibilityTimeout = 300 // 5 mins to process before retry
});
foreach (var message in response.Messages)
{
try
{
await ProcessAsync(message.Body);
// Only delete after successful processing
await sqsClient.DeleteMessageAsync(queueUrl, message.ReceiptHandle);
}
catch
{
// Don't delete - message will become visible again after timeout
// Optionally, change visibility timeout to retry sooner
await sqsClient.ChangeMessageVisibilityAsync(queueUrl,
message.ReceiptHandle, visibilityTimeout: 0);
}
}
Den smarta SQS-tricken ApproximateNumberOfMessages för att övervaka ködjupet och automatiserat
var attributes = await sqsClient.GetQueueAttributesAsync(new GetQueueAttributesRequest
{
QueueUrl = queueUrl,
AttributeNames = new List<string> { "ApproximateNumberOfMessages" }
});
var depth = int.Parse(attributes.Attributes["ApproximateNumberOfMessages"]);
if (depth > 1000)
{
// Signal to scale up consumers
await TriggerAutoScalingAsync();
}
NATS JetStream: Flow Control Built In
NATS är en lightweight, hög JetStream är NATS, ', byggt, -, i en uthållig lager som tillhandahåller hållbara flöden. ,, exakt, M SK3, först levererad,, och —, vilket är viktigt för oss,explicit flödeskontroll.
NATS JetStream har explicit flödeskontroll med konsumenterkännelser och max upphängande meddelandesgränser:
var js = connection.CreateJetStreamContext();
var subscription = js.PushSubscribeAsync("orders.>", (sender, args) =>
{
try
{
ProcessMessage(args.Message.Data);
args.Message.Ack();
}
catch
{
args.Message.Nak(); // Negative ack - redeliver
}
}, new PushSubscribeOptions.Builder()
.WithConfiguration(new ConsumerConfiguration.Builder()
.WithMaxAckPending(100) // Max unacked messages - THIS is backpressure
.WithAckWait(30000) // 30 seconds to ack
.Build())
.Build());
Den vanliga tråden
Notera vad alla dessa system har gemensamt
- Explicita lotsmängder/konkurensgränser - du kontrollerar hur mycket du
- Erkänning-baserad ström - meddelanden stannar tillgängliga tills du bekräftar bearbetandet
- Timeout-baserad återhämtning - om du misslyckas, kommer meddelanden att återkomma för att pröva igen
- Längsstyrning - kan du alltid fråga
Syntaxen skiljer sig, men dansen är densamma. "" Här är vad jag kan hantera. Hur mycket kan jag hantera? "" "" Sa till mig när jag klarade det. ""
C# Kodexexamlar
Regnerkant, släpper ' in i koden . Här är praktiska exempel på att implementera och reagera på baktrycket i dina C-applikationer
Uppföljning av ködepten
Först och främst: —, du kan , ', man kan inte sköta vad du kan, ', man cannot mäta, ., här kan man se hur många meddelanden väntar på kö:
var queue = channel.QueueDeclare(
queue: "tasks",
durable: true,
exclusive: false,
autoDelete: false);
Console.WriteLine($"Messages ready: {queue.MessageCount}");
// React to queue depth
if (queue.MessageCount > 1000)
{
Console.WriteLine("Queue backing up - consider throttling producers");
}
Den här biten kontrollerar hur många meddelanden väntar.
Publisher bekräftar
Publisher bekräftar låter dig veta när RabbitMQ har fått och bearbetat din meddelande framgångsrikt. Om erkännelser saktar nerM SK1 så är det' ett tydligt tecken på baktryck
// Enable publisher confirms
channel.ConfirmSelect();
var body = Encoding.UTF8.GetBytes("Hello, Queue!");
channel.BasicPublish(
exchange: "",
routingKey: "tasks",
basicProperties: null,
body: body);
// Wait for confirmation - timeout indicates backpressure
bool confirmed = channel.WaitForConfirms(TimeSpan.FromSeconds(5));
if (!confirmed)
{
Console.WriteLine("Message not confirmed - broker may be under pressure");
}
Om RabbitMQ kämpar med problem
För högupplösta scenarior, -, m sk1 vill du ha asynkrona bekräftelseer, mnsk3
channel.ConfirmSelect();
var outstandingConfirms = new ConcurrentDictionary<ulong, string>();
channel.BasicAcks += (sender, ea) =>
{
if (ea.Multiple)
{
var confirmed = outstandingConfirms.Where(k => k.Key <= ea.DeliveryTag);
foreach (var entry in confirmed)
{
outstandingConfirms.TryRemove(entry.Key, out _);
}
}
else
{
outstandingConfirms.TryRemove(ea.DeliveryTag, out _);
}
};
channel.BasicNacks += (sender, ea) =>
{
// Message was rejected - implement retry logic
Console.WriteLine($"Message {ea.DeliveryTag} was nacked - broker under pressure");
// Back off before retrying
};
Försök igen med Exponential Backoff
När du upptäcker baktrycket, så är det värsta du kan göra att direkt pröva om full hastighet. Det är som att reagera på trafikstockning genom att trycka på acceleratorn hårdare. Istället, implementera exponentiell bakslag.
public async Task PublishWithBackpressureAsync(
IModel channel,
byte[] body,
int maxRetries = 5)
{
int attempt = 0;
while (attempt < maxRetries)
{
try
{
channel.ConfirmSelect();
channel.BasicPublish(
exchange: "",
routingKey: "tasks",
basicProperties: null,
body: body);
if (channel.WaitForConfirms(TimeSpan.FromSeconds(5)))
{
return; // Success
}
throw new Exception("Publish not confirmed");
}
catch (Exception ex)
{
attempt++;
if (attempt >= maxRetries)
{
throw new Exception($"Failed to publish after {maxRetries} attempts", ex);
}
// Exponential backoff: 1s, 2s, 4s, 8s, 16s
var delay = TimeSpan.FromSeconds(Math.Pow(2, attempt - 1));
Console.WriteLine($"Backpressure detected - retry {attempt} after {delay}");
await Task.Delay(delay);
}
}
}
Det här påminner om HTTP 's 429 | (Too många begäran ) mönster M SK4 Istället för att hamma brokern, sätter vi stilla innan vi försöker igen MSC6 och ger systemet tid att återhämta sig
Att använda kanaler för In-Process baktryck
Om du bygger en inre ledning, så är det så här: Channel<T> erbjuder elegant baktrycksstöd:
// Create a bounded channel - backpressure is automatic
var channel = Channel.CreateBounded<WorkItem>(new BoundedChannelOptions(100)
{
FullMode = BoundedChannelFullMode.Wait // Block producer when full
});
// Producer - will automatically wait when channel is full
async Task ProduceAsync(ChannelWriter<WorkItem> writer)
{
for (int i = 0; i < 10000; i++)
{
var item = new WorkItem { Id = i };
// This awaits if the channel is at capacity
await writer.WriteAsync(item);
Console.WriteLine($"Produced item {i}");
}
writer.Complete();
}
// Consumer - processes at its own pace
async Task ConsumeAsync(ChannelReader<WorkItem> reader)
{
await foreach (var item in reader.ReadAllAsync())
{
// Simulate slow processing
await Task.Delay(100);
Console.WriteLine($"Processed item {item.Id}");
}
}
// Run both concurrently
await Task.WhenAll(
ProduceAsync(channel.Writer),
ConsumeAsync(channel.Reader)
);
Den begränsade kanalen använder automatiskt baktrycket, —, producent blockerar när kanalen är full.,, saktar ned på ett naturligt sätt för att matcha konsumentens hastighet.
Ett fullständigt baktryck-Ärligt publicerat
Här är ett mer komplett exempel som sammanför övervakning. ständiga meddelanden för att se till att meddelanden överlev دلال restarts:
public class BackpressureAwarePublisher : IDisposable
{
private readonly IConnection _connection;
private readonly IModel _channel;
private readonly string _queueName;
private readonly int _queueDepthThreshold;
public BackpressureAwarePublisher(
string hostName,
string queueName,
int queueDepthThreshold = 1000)
{
// See https://www.rabbitmq.com/docs/connections for connection best practices
var factory = new ConnectionFactory { HostName = hostName };
_connection = factory.CreateConnection();
_channel = _connection.CreateModel(); // Creates a channel - see https://www.rabbitmq.com/docs/channels
_queueName = queueName;
_queueDepthThreshold = queueDepthThreshold;
_channel.QueueDeclare(
queue: queueName,
durable: true,
exclusive: false,
autoDelete: false);
_channel.ConfirmSelect();
}
public async Task<bool> PublishAsync(byte[] body, CancellationToken ct = default)
{
// Check queue depth first
var queueInfo = _channel.QueueDeclarePassive(_queueName);
if (queueInfo.MessageCount > _queueDepthThreshold)
{
Console.WriteLine($"Queue depth {queueInfo.MessageCount} exceeds threshold - applying backpressure");
// Wait for queue to drain a bit
while (queueInfo.MessageCount > _queueDepthThreshold * 0.8)
{
await Task.Delay(1000, ct);
queueInfo = _channel.QueueDeclarePassive(_queueName);
}
}
// Publish with retry
for (int attempt = 1; attempt <= 3; attempt++)
{
try
{
var properties = _channel.CreateBasicProperties();
properties.Persistent = true;
_channel.BasicPublish(
exchange: "",
routingKey: _queueName,
basicProperties: properties,
body: body);
if (_channel.WaitForConfirms(TimeSpan.FromSeconds(5)))
{
return true;
}
}
catch (Exception ex)
{
Console.WriteLine($"Publish attempt {attempt} failed: {ex.Message}");
}
if (attempt < 3)
{
await Task.Delay(TimeSpan.FromSeconds(Math.Pow(2, attempt)), ct);
}
}
return false;
}
public void Dispose()
{
_channel?.Dispose();
_connection?.Dispose();
}
}
De bästa metoderna
För en överblick på hur man bygger pålitliga system med RabbitMQ,, se den officiella Tillförlitlighetshandboken.
Stanna lugna
Stanna inte i panik när köerna växer. 1. En bit djup är normalt och hälsosamt. 2. Det betyder att din system absorberar lastspetsar graciöst. 3. målet är att stabila kö som inte växer obegränsat
Titta på ködjupet över tiden... Leta efter trender.,, inte några korta bilder, .. En kö som consistentt står vid 100, meddelanden är bra.
Var pragmatisk
Applicera mönster på ett pragmatiskt sätt. Alla meddelanden behöver inte utlagaren bekräftar . Alla köer behöver inte sofistikerad bakpressurbehandling M SK2 En kö som processar 10 meddelanden per timme behöver förmodligen inte ' samma dragbarhetsteknik som en enda processing | 10,000 | per sekund
Fråga dig själv.: " Vad är den faktiska kostnaden ifall detta meddelande förloras eller fördröjers? ?" Om svaret är: ", inte mycket, ,", inte ', inte över -, ingenjör ., om svaret är "" ", stor påverkan på finansen eller datans integritet "", ,", investera i rätt backpressurbehandling.
Skala Smart
När köer håller på att komma tillbaka, så är svaret: "'". Det är inte alltid så. " "", "" lisää fler producenter "", ." "", det är som att försöka fixa trafikstockning genom att lägga till fler bilar.
Tänk på:
- Skala konsumenter först - kan du lägga till fler anställda för att processa backlogn
- Se om bottlenecks - är en långsam nedströms beroende som orsakar bakställningen
- Koll där möjligt - kan konsumenter processa flera meddelanden på samma gång
flowchart TD
A[Queue Growing] --> B{Consumer<br/>Saturated?}
B -->|Yes| C[Add Consumers]
B -->|No| D{Downstream<br/>Bottleneck?}
D -->|Yes| E[Fix/Scale Downstream]
D -->|No| F{Can Batch<br/>Process?}
F -->|Yes| G[Implement Batching]
F -->|No| H[Accept Higher Latency<br/>or Reduce Load]
style C stroke:#10b981,stroke-width:2px
style E stroke:#f59e0b,stroke-width:2px
style G stroke:#0ea5e9,stroke-width:2px
övervakning och varning
Använd RabbitMQ Management- Plugin eller HTTP API för komplext övervakning. Installera varningar för:
- ködjuven överskrider tröskeln
- Tillförsel av konsumenter ökar
- Publisher bekräftar att tiden är ute
- Verbinding blockerade händelser
Du vill veta om baktryck före det blir en kris, inte när ditt system redan har gått under,
Slutsatsen
"Backpressure" är inte bara "rate limiting", det är en överlevnadstaktik. . Genom att hantera det som ett samtal mellan producent och konsument, skapar man system som är motståndskraftiga under tryck.
De viktigaste insikterna:
- Backpressure är feedback - producenter och konsumenter samarbetar för att hitta ett hållbart genomskott
- Monitora ködjupet - du kan ' kan inte sköta vad du kan
- Använd förläggare bekräftar - vet när brokern kämpar
- Implementera exponentiella backoff - inte hammera ett system som ' redan är under tryck
- Skala konsumenter, inte bara producenter - fixa flaskhalvan , inte symptomet
När ditt system säger "" ", jag, ', jag är full "", ",, ge mig en minut" och "" ,", "" är det rätta svaret.
Stanna lugna när köerna växer lite. , och kom ihåg att ett system med elegant baktryck är oändligt bättre än ett som