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
在压力下: 如何排队系统处理后压, CMS K1例
Sunday, 23 November 2025
后压是分布式系统不响的英雄@. @ it'}让您的队列不会在裁缝中爆裂, 当制片人发信息的速度快于消费者能够通过它们咀嚼的速度时#._简略地说:}(itMS K4})这个系统表示“"}等一下<"}当事情变得太忙的时候 @.}
在这里,'的东西 : 本条中的手法适用于 每个 您所命名的队列与服务总线@— @RabbitM,}(卡夫卡),}Azure Service Bus* ,} AWSSQSMS K4}NATSMPK5}您所命名的具体细节有差异
一种忏悔 即使大多数高级开发商都实施适当的后压处理@.}他们也建立快乐的-path系统, 在 dev 和 streading ,}然后想知道为什么在黑色星期五期间生产会下降?
后压是
当消费者落后于“.”时, 反压是一个反馈回路, 它会让生产者慢下来。 想象一下它就像滑行路上的交通灯光: “—”你就可以在“'”上堆到高速公路上, 当你喜欢“MS K4”的时候,
快速制片人若没有回压@, @ a fast professor 将会淹没在队列中堆积的慢速消费信件@ . @ ,} 记忆力将耗尽 *, 最终你的系统会跌落到 <.# Backpressure MS K5{speaty on @"} 在灾难袭击前 @MSSK7}
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
后压的美丽之处在于 对话框 消费者信号 @"I'm full_,给我一分钟#"和制片人回应@"没有问题$,I'}等待礼貌的,协作}, 让每个人都不要跌倒 在.
RabbitMQ 处理后压如何
兔子兔MQ 是一个执行此命令的 Open+- 源代码消息中介 高级信件排队协议@(AMQP)“.”和“I(”是公司从新创办企业到Bloomberg ,Pivotal,和VMware@MSK5等企业使用的信息最广泛的信息经纪人之一。
兔子超赞的
- 复杂路线 -Q Exchanges @,Q 绑定@,Q 和路由密钥让您建立精密的信息路由:%.Q 想要在信头_?}(直接针对特定消费者)的基础上向多个队列发送信息 交换模式的交流、交换和 (pirect,fanoutMS K2 主题,headers)}你是否覆盖过.
- 可靠性担保 - 出版商确认, 消费方承认,和 持久性信息 表示您可以建立无法接受信息丢失的系统@. @%
- 灵活部署 - 运行在开发的单一节点上@,或部署 a 组群 与 法定人数队列 生产中高供应量
- 礼宾支持 @-超越AMQP,兔子支持 MQTT MQT TT # (#IOT#I),# 斯托姆 (simple 文本协议@),}并有一个 溪流 Kafka- 的功能类似附件@ - @ 仅logs @ .}
何时考虑替代品@: @% 如果您需要使用更简单的路由“( 百万条消息”来进行大规模输送, 请用您的管理服务 “(Azure Service Bus@,} AWSSQS) 降低运行的顶部“MS K12 ”
RabbitMQ 拥有若干已建的“-in”处理后压机制, rabbitMQ 文件 当我们去“.”时,它会链接到特定的页面。
流动控制
当 RabitM' 内存用量或队列深度超过配置的阈值@ , 它激活 流量控制此临时封隔出版商连接@— @publishers can'}在经纪人清理完足够积压的邮件前, 内存提醒 和 磁盘警报器 触发电流控制
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
这里的关键洞察力是,RabbitMQ 在压力下只发送消息 =—} 它会减慢源@.}这是比静悄悄地丢弃数据更文明的方法 .
消费者致谢
消费者控制速度 承认的确认 'QT ACK K5 队列增速=— 最终触发上游流量控制=MSC7}
您也可以使用 附加权限值 以控制消费者在飞行中能够同时收到多少未确认的信息@.}这样可以防止一个慢速的消费者存储信息 @.
// Set prefetch count to limit unacknowledged messages
channel.BasicQos(prefetchSize: 0, prefetchCount: 10, global: false);
这告诉RabbitM:+ " 一次只给我发@10 信息$只要我加了点",你就可以发送更多"MSKO5}'}消费者明确表示它能承受多少压力{.} .NET 客户文件 详细覆盖 API+.
其它系统如何处理后压
这些模式是通用的 @,, 但执行方式却不同 @MS K1 这里#' 其它流行信息系统如何处理同样的问题?
Kafka:消费者 □-控制投票
阿帕契卡卡夫卡 是一个分布式事件流平台,设计用于高@- @topput_,}错误 @-#容恕性讯息.}和传统信息经纪人不同{,}卡夫卡在附件>-}仅日志中存储消息并让消费者跟踪自己的位置\ (offsetsMS K7}
卡夫卡采取了完全不同的方法 消费者 拉拉 而不是让它们推动信件@ . @ 这样使回压隐含 @ : @ 如果一个消费者不这样做的话 '} t polks@ I, 它不接受消息_ MS K4} Kafka 消费者文件 投票模式详情@.
// 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
}
如果一个消费者落后于“,” ,您可以在该组中增加更多的消费者,而分区获得再分配=.}后压则成为缩放决定_.
// 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: 同步信件控制
服务客车 是微软' 完全管理的企业信息经纪人,配有队列并出版*- 注册题材 . ItMS K3 设计用于在云层中解开应用程序和服务
服务客车使用a MaxConcurrentCalls 设置“' @%s beautifully simple”\—}控制您处理器同时处理多少信件@.}后压是自动 @.} 信息处理文件 包含详细性能调控的功能@. @%
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();
服务客车也支援 届会各届 被命令处理和 $-}字母队列 对于多次失败的信件 —}两者都很重要 当事情出错时
AWS SQS: 可见度超时舞蹈
Amazon 简单队列服务 @(@SQS) @% 是一个来自 AWS @.}的完全管理的信件排队服务, 它提供两种队列类型@:标准 @ (Best-#effort 命令{,}在-lent-once delietion_)和FIFO MS K8exactly-ONce programing},}严格命令<).}
SQS 用途 可见度超时 当您接收到一条消息 , n它成为其他消费者的看不见@.}如果你在时间中做\ I'\ t删除它..."," 它会重新出现,让其他人尝试\ O. >
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);
}
}
聪明的 SQS trick: 使用 ApproximateNumberOfMessages 以监测队列深度和自动@- @ 比例消费者 @ :#
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 : 流动控制装置
海军 是一个轻量级的 , 高 MS K1 性能云 -native信息系统. 喷气流 是 NATS's 建造的MS K1#in 持久性层,增加耐久性流 ,} 确切地说-once sufferation,和{—}对于我们来说很重要 ~—直径流量控制.
NATS JetStream具有明确的流量控制,包括消费者确认和最多待发信件限制@:
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());
共同线索
注意所有这些系统的共同点 @:@
- 明度批量/货币限额 - 你控制你多愿意处理
- 致谢=-基流 -}在您确认处理前, 信件一直可用 。
- 超时@-基于恢复 - 如果您失败了 @, 信件返回重试
- 深度监测 - 你总是可以问" "I?""的备份吗?
语法不同 @,}但舞步相同#: . 告诉我 I''"已经处理过它的时候 如果我不做'"
C# 代码示例
在您的 CQ# 应用程序中执行和应对后压的实际实例@. @%
队列深度监测
首先 — 您可以控制您能管理什么 您可以% K2 @ t量度@ . @ 在这里 _'}这里如何检查有多少信件在等待 队列:
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");
}
此片段检查有多少消息等待@ . @ 如果计数在攀爬 @ MS K1, 那么% '{ 提示您加速生产商或扩大消费者范围@ I._ Doñ'} 不要担心过频繁的检查@ MPK5a 定期健康检查通常就足够了 @.}
发布器确认
出版商确认 当 RabbitMQ 成功接收和处理您的信件时, 请告知您@ . @ 如果确认速度放慢了 @ MS K1} _ ' k4 a clear sign of backpressure_ :}
// 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");
}
如果 RabitMQ 正在挣扎 @,确认需要更长的时间或完全超时@. 您的制片人可以使用此信号后退, 而不是用更多压力堆积 #.#
对于高“-”的通量假想 “, ”您想要非同步确认“MS K3 ”
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
};
使用指数后退重试
当您检测到回压@ , @ 最坏的操作是立即全速重试@ I.} 这就像按下加速器来应对交通阻塞吗?
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);
}
}
}
这个模仿 HTTP ' @%s @ MS K1 @ @ @ I( @ Too many request @ MPK3}模式@ ._ 而不是敲打经纪人@,# 我们暂停, 然后重试,# 让系统有时间恢复 #. @
使用通道用于 Inç- process 后压
如果您正在建造一个内部管道, 请在您的应用程序中将您所有的消费方 ), @ .NET's Channel<T> 提供优雅的后压支持@: @%
// 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)
);
连接的频道自动应用回压@ — @ 生产区块, 当该频道是全Q,} 自然减速以匹配消费者'} 步调.} 不要求手动抽动\ MS K4}
完整后压@ - @Aware 发布器
这里的'* 是一个更完整的例子, 持久性信息 来确保信件在经纪人身份中有效重现@: @%
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();
}
}
最佳做法
有关使用RabitM,建立可靠系统的全面概况,请见官方网站 可靠性指南.
保持冷静
当队列增长时, Don't 恐慌@ .# 有一点点深度是正常的和健康的 @ MS K2\it 表示您的系统正在以优雅的方式吸收负载加注Q.}目标为 @ MPK4t 一个空的队列@; it'}a 稳定 队列中无约束的增减@ ' @ title
监视队列的深度@. 寻找趋势 @ , 而非快照 @ MPK2 一个在 @ I100 信件中始终一致的队列\ MS K3 是很好的@ . 一个在过去一个小时里从 @ MCK7 增长到 @10,000 需要注意 @ OMK9
实用化
实际应用模式@. @% 不是每个信件需要的出版商都确认 .}不是每一个队列都需要复杂的后压处理QZMK2}一个每小时处理 &10}消息的队列 可能不需要和每秒处理一次的#10,000}一样的恢复力工程
询问您自己@: @ @ " @ 什么'} 如果此信件丢失或被延迟是否实际成本@ MS K3 @ 如果答案是 @ I" @ non much @ MPK5 @ don' @ tover @- @ engineerQ. @ 如果回答是“ " @ 重要财务或数据完整性影响” @ OMK10_ 投资适当的回压处理 @ MIK11 @
规模智能
当队列支持“,”时, 答案总是“ '” 。 “ MS K2” 不是总能添加更多的制片人@ MSC3 ” 这“ I'” 就像试图通过增加汽车来修补交通阻塞 @ . ”
考虑一下:
- 以消费者为首 - @% 您可以增加更多工人来处理积压的工作吗? @?@%
- 检查瓶颈 - 是一个缓慢的下游依赖关系 导致备份?
- 尽可能批次 @- 消费者可以同时处理多个消息@?
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
监测和警报
使用 RabitMQ 管理插件 或 HTTP API 设置用于 :的警报
- 队列深度超过阈值
- 消费者的时滞增加
- 出版商确认时出时间
- 连接屏蔽事件
你想知道后压的情况 之前 而不是当你的系统... ...'...
结论结论
后压是'}你所建立的系统 在压力下保持抗御力的系统=□.}*
关键洞察力:
- 后压是反馈 *- 生产者和消费者合作寻找可持续的投入
- 监视队列深度 - 您可以控制您能管理的东西 @ MS K2 t量度
- 使用出版商确认 {\fn黑体\fs22\bord1\shad0\3aHBE\4aH00\fscx67\fscy66\2cHFFFFFF\3cH808080}我知道经纪人什么时候在挣扎
- 执行指数反转 @ -_ don'_ t敲一个已经承受压力的系统@'%s
- 不仅是生产商, # 修复瓶颈 # # M-# # 不是症状 #
当您的系统说“"I'm full,给我一分钟”时, 正确的回答是:“"没有问题吗?,I'll 等待'."#那"'有良好分配的系统的精髓-}(MSPK10polite},协作=,和具有弹性的.
当队列增长有点,时保持冷静 记住:}在优雅的后压下,一个系统比这个系统要好得多