المفاهيم
هنفهم إيه؟ لو order اتسجل لكن خدمة الإيميل وقعت، هنشوف إزاي نكمل الشراء ونوصل الشغل لاحقاً، وإزاي نمنع فقد الرسالة أو تكرار الأثر عند إعادة المحاولة. هنتدرج من producer و وqueue إلى التأكيدات، المستهلكين، والاسترجاع بعد الفشل.
قبل ما تبدأ: producer يرسل رسالة، وqueue مكان ينتظر فيه الشغل، وconsumer يعالجه. احفظ الفرق بين حفظ order في قاعدة البيانات وإرسال رسالة للشبكة: العمليتان منفصلتان وقد يفشل بينهما التطبيق. معرفة وقواعد البيانات تكفي؛ المصطلحات الباقية ستُعرّف هنا.
المصطلحات اللي تحتها خط منقّط تقدر تضغط عليها لمعرفة معناها، وترجع تكمل من نفس مكانك.
#Sync و async messaging و دور الـ brokerSynchronous calls, asynchronous messaging and brokers
لو checkout بينتظر الإيميل والتحليلات، تعطل خدمة جانبية ممكن يعطّل شراء سليم. الـ synchronous مناسب لما محتاج قرار دلوقتي؛ messaging مناسب لشغل يقدر يحصل بعد تثبيت الطلب.
الـ بيستقبل ويوجّه ويخزّن حسب إعداداته. مش كل نشر durable، ومش كل قبول معناه إن المستهلك خلّص. فرّق بين command بيطلب فعل من مسؤول معين، و event بيعلن حقيقة حصلت وممكن كذا subscriber يهتم بيها.
Checkout -> database: order + outbox row in one transaction // (1)
Dispatcher -> broker -> email queue -> email worker // (2)
-> analytics queue -> analytics worker
- (1) الـ HTTP request بيرد بعد الحفظ المحلي، مش بعد إرسال الإيميل.
- (2) كل subscriber ليه queue عشان يحصل على نسخة؛ workers على نفس queue بيتنافسوا على الرسائل.
التكلفة: ، و duplicates، وعمليات تشغيل أكتر. لازم pending state واضحة للمستخدم، ومراقبة عمر الرسائل. راجع topic 12 لتفاصيل و consistency؛ messaging مش ضمان تلقائي للـ availability.
#RabbitMQ: المكونات والـ exchangesRabbitMQ components and exchange types
الـ producer بينشر للـ exchange ومع الرسالة routing key. الـ binding بيربط exchange بـ queue بقواعد routing، والـ consumer بيقرأ من queue. الـ connection غالباً طويلة العمر، وجواها channels أخف؛ الـ virtual host عزل منطقي بصلاحيات منفصلة.
| Exchange | قاعدة ومثال |
|---|---|
| direct | تطابق حرفي: payment.failed يوصل لكل queue مربوطة بنفس المفتاح. |
| topic | order.*.eg يطابق order.paid.eg. النجمة كلمة واحدة، و order.# يطابق صفر أو أكتر من الكلمات المفصولة بنقط. |
| fanout | نسخة لكل queue مربوطة، ويتجاهل routing key؛ مناسب لإشعار الإيميل والولاء. |
| headers | يطابق headers زي format=pdf و region=eg؛ x-match=all لكل الشروط، و any لأي شرط. |
| default: "" | direct exchange موجود مسبقاً؛ كل queue مرتبطة تلقائياً باسمها، فـ routing key = اسم الـ queue. |
الـ exchange مش هو مكان تخزين الـ backlog. الـ كمان exchange، ولازم queue مربوطة بيه لو عايز تحتفظ بالفشل. الأنواع دي الأساسية في AMQP 0-9-1؛ RabbitMQ عنده أنواع وإضافات تانية.
exchange=orders (topic), routingKey=order.paid.eg
binding=order.*.eg -> regional-orders
binding=order.# -> all-orders
binding=order.* -> no match (only two words)
exchange="", routingKey=email -> queue named email
#RabbitMQ.Client 7: النشر والـ confirmsRabbitMQ.Client 7: publishing and confirms
التطبيق اللي بينشر الرسالة اسمه publisher، وRabbitMQ هو اللي يستقبلها ويوجهها للـ queue. نجاح الكتابة على socket معناه إن البيانات خرجت من تطبيقك، مش إن الـ broker قبلها أو احتفظ بيها. Publisher confirm رد من الـ broker على نتيجة النشر؛ وهو مختلف عن consumer ack، إشارة من المستهلك بعد ما يخلص معالجة رسالة. المثال برنامج console بـ RabbitMQ.Client 7.x ويفترض broker محلي؛ production يحتاج وصلاحيات محدودة وإعدادات خارج الكود.
using RabbitMQ.Client;
using System.Text;
var factory = new ConnectionFactory { HostName = "localhost" };
await using var connection = await factory.CreateConnectionAsync();
var options = new CreateChannelOptions(
publisherConfirmationsEnabled: true,
publisherConfirmationTrackingEnabled: true); // (1)
await using var channel = await connection.CreateChannelAsync(options);
await channel.QueueDeclareAsync(
queue: "orders", durable: true, exclusive: false, autoDelete: false,
arguments: new Dictionary<string, object?>
{
["x-queue-type"] = "quorum" // (2)
});
var properties = new BasicProperties
{
Persistent = true,
MessageId = Guid.NewGuid().ToString()
};
using var timeout = new CancellationTokenSource(TimeSpan.FromSeconds(5));
await channel.BasicPublishAsync(
exchange: "", routingKey: "orders", mandatory: true,
basicProperties: properties, body: Encoding.UTF8.GetBytes("order-created"),
cancellationToken: timeout.Token); // (3)
- (1) confirmations و tracking بيخلّوا await يراقب نتيجة النشر؛ ده 7 async، مش
IModelالقديم. - (2) queue durable من نوع ؛ ضمان التحمل مرتبط بتوبولوجي الـ cluster وتوافر replicas، مش الاسم بس.
- (3) mandatory يكشف عدم وجود route. مع tracking، return أو nack يفشل النشر. timeout معناه النتيجة ممكن تكون مجهولة، فاحتفظ بنفس message ID في retry.
#Acks و nack و prefetch و DLXAcknowledgments, prefetch and dead lettering
لما يسلّم رسالة للـ consumer، المستهلك يبعت ack (acknowledgment) بعد ما يخلص بنجاح علشان broker يعرف إنه يقدر يشيل التسليم من الانتظار. لو المستهلك حفظ التغيير في database وبعدين وقع قبل ack، broker ممكن يعيد الرسالة؛ عشان كده الـ handler لازم يتحمل التكرار من غير ما يكرر الأثر. Prefetch حد للرسائل المسلّمة اللي لسه ماعليهاش ack؛ مش هو نفسه عدد handlers اللي شغالة بالتوازي.
// Inside a BackgroundService: channel and HandleAsync are application collaborators.
// Provision the queue and a bound DLQ first; configure its DLX through broker policy.
using RabbitMQ.Client;
using RabbitMQ.Client.Events;
await channel.BasicQosAsync(0, prefetchCount: 16, global: false); // (1)
var consumer = new AsyncEventingBasicConsumer(channel);
consumer.ReceivedAsync += async (_, delivery) =>
{
byte[] body = delivery.Body.ToArray(); // (2)
try
{
await HandleAsync(body, stoppingToken);
}
catch (InvalidDataException)
{
await channel.BasicNackAsync(
delivery.DeliveryTag, multiple: false, requeue: false); // (3)
return;
}
await channel.BasicAckAsync(delivery.DeliveryTag, multiple: false); // (4)
};
await channel.BasicConsumeAsync("orders", autoAck: false, consumer: consumer);
await Task.Delay(Timeout.Infinite, stoppingToken); // (5)
- (1) حد 16 unacked لكل consumer في الإعداد ده؛ الصغير يحسن التوزيع والكبير ممكن يزود واستهلاك الذاكرة.
- (2) الـ body مملوك للـ client وصلاحيته مرتبطة بالـ callback؛ النسخ مطلوب لو هنحتفظ بيه.
- (3) رفض نهائي من غير requeue؛ يروح لو متظبط، وإلا ممكن يضيع.
- (4) ack بعد النجاح وبنفس channel؛
multiple:falseبيتجنب ack لرسائل تانية لسه ماخلصتش. - (5) صاحب الـ channel يفضل عايش. shutdown يعمل cancel للـ consumer وينتظر in-flight handlers قبل disposal.
المثال بيفصل permanent failure بس. في transient failure لازم callback exception handling واضح، و bounded retry مع delay أو retry queue؛ ماتعتمدش على exception غير معالجة إنها هتعمل requeue لوحدها. إغلاق channel بيرجع unacked messages، لكن retry فوري بلا حد يعمل hot loop. راقب وحط replay workflow.
#Kafka: log و partitions و offsetsKafka: logs, partitions and offsets
لو عايز كذا نظام يعيد قراءة نفس التاريخ، queue بتمسح بعد ack مش دايماً النموذج المناسب. Kafka بيقسم topic لـ partitions، وكل partition سجل مترتب، وكل record ليه offset محلي. القراية مابتمسحش الحدث؛ retention بالوقت أو الحجم، و compaction إعداد مختلف يحتفظ بأحدث قيمة لكل key ضمن قواعده.
في consumer group التقليدية، كل partition لها consumer نشط واحد من نفس group. Groups مختلفة تقرأ نفس البيانات باستقلال. Commit قبل المعالجة ممكن يضيّع effect؛ بعد المعالجة ممكن يكررها بعد crash. الـ committed offset بيمثل موضع الاستكمال، مش إثبات إن كل الأنظمة الخارجية اتحدثت.
topic: orders
partition 0: offset 0 [order A v1] offset 1 [order A v2] // (1)
partition 1: offset 0 [order B v1]
group invoicing: consumer X -> p0, consumer Y -> p1 // (2)
group analytics: consumer Z -> p0 and p1 // (3)
- (1) استخدم order ID كـ key مع partitioner ثابت؛ تغيير عدد partitions ممكن يغيّر توزيع المفاتيح. الترتيب جوه partition مش topic كلها.
- (2) في النموذج ده partitions بتحدد سقف parallel consumers في group؛ زيادة العدد تسيب consumers فاضية.
- (3) analytics ليها offsets مستقلة، وتقدر تعمل replay لو البيانات لسه retained.
Producer retries وإعدادات producer والـ in-flight requests بتأثر على الحفاظ على الترتيب. Processing parallel جوه partition أو تخطي failed event ممكن يكسر ترتيب الـ effects.
#RabbitMQ و Kafka و Azure Service BusRabbitMQ, Kafka and Azure Service Bus
| الجانب | RabbitMQ queues | Kafka | Azure Service Bus |
|---|---|---|---|
| النموذج | بتوجيه مرن و ack لكل message | partitioned retained log | managed enterprise broker: queues و topics و subscriptions |
| بعد المعالجة | ack يشيل الرسالة من queue | offset بيتحرك والـ record يفضل حسب retention | Complete يشيل الرسالة من queue أو subscription |
| Fan-out | queue لكل subscriber عبر exchanges | consumer group لكل subscriber | subscription لكل مستلم في topic |
| الترتيب | single active consumer ومعالجة sequential؛ retries محتاجة تصميم | داخل partition | sessions لترتيب معالجة الرسائل المرتبطة |
| أنسب استخدام | work queues و commands وتوجيه مخصص | streaming و replay و pipelines | تكامل Azure بعمليات تشغيل مُدارة و و sessions |
RabbitMQ عنده Streams كمان؛ المقارنة هنا للـ queues التقليدية. الاختيار حسب replay و routing و وخبرة التشغيل والتكلفة، مش ادعاء إن واحد أسرع دائماً. Features زي duplicate detection في Service Bus ليها نطاق وزمن محدودين، مش ضمان exactly-once لكل business effect.
#Transactional outbox و inboxTransactional outbox and inbox
- أنشأت الطلب في قاعدة البيانات، وبعد الحفظ حاولت إرسال OrderCreated، لكن العملية تعطلت قبل الإرسال. الطلب موجود وخدمة الشحن لا تعرف عنه.
- داخل نفس المعاملة المحلية، احفظ الطلب وصفًا في جدول يمثل الرسالة المطلوب إرسالها. عامل منفصل يقرأ الجدول ويرسل الرسائل ثم يسجل تقدم الإرسال.
- لو العامل أرسل ثم تعطل قبل تسجيل النجاح، قد يرسل الرسالة مرة ثانية. لهذا المستهلك يحتاج تحمل التكرار؛ outbox يغلق فجوة الحفظ والإرسال، ولا يجعل كل أثر خارجي يحدث مرة واحدة تلقائيًا.
الحفظ وبعدين النشر عمليتين منفصلتين: crash بينهم يضيّع event، والنشر الأول ممكن يعلن عن order مااتحفظتش. خزّن business change و outbox row في نفس database ، وخلّي dispatcher ينشر بعدين. عند المستهلك، سجّل message ID مع الـ effect في transaction واحدة.
// EF Core fragments; entities and serializer are application code.
await using var tx = await db.Database.BeginTransactionAsync(ct);
db.Orders.Add(order);
db.Outbox.Add(new OutboxMessage(messageId, Serialize(order))); // (1)
await db.SaveChangesAsync(ct);
await tx.CommitAsync(ct);
// Dispatcher: claim a row safely before publishing; broker confirms required.
await PublishWithConfirmAsync(row.Id, row.Payload, ct); // (2)
row.MarkSent();
await db.SaveChangesAsync(ct);
// Consumer uses a different DbContext and transaction.
await using var receiveTx = await db.Database.BeginTransactionAsync(ct);
db.Inbox.Add(new InboxMessage(consumerName, messageId)); // (3)
await db.SaveChangesAsync(ct);
await ApplyDatabaseEffectAsync(db, message, ct); // (4)
await db.SaveChangesAsync(ct);
await receiveTx.CommitAsync(ct);
// Only now acknowledge the delivery.
- (1) الاتنين ينجحوا أو يفشلوا مع بعض؛ outbox فيها ID ثابت و type و payload ووقت وحالة محاولات.
- (2) لو وقع بعد confirm وقبل MarkSent، الرسالة هتتبعت تاني. Dispatcher متعدد workers محتاج claim آمن و leases وحدود batch.
- (3) اعمل unique constraint على consumer name و message ID. لو حصل duplicate-key violation المحدد ده، rollback واتأكد إنه duplicate ملتزم فعلاً وبعدين ack؛ باقي أخطاء ماتتبلعش.
- (4) effect في نفس الـ DB transaction، مش charge. لو transaction فشلت، inbox row تتراجع معاها.
#Delivery guarantees و idempotency والترتيبDelivery guarantees, idempotency and ordering
| الضمان | النتيجة |
|---|---|
| At-most-once | ممكن تضيع رسالة؛ مفيش إعادة تسليم في المسار ده. |
| At-least-once | retry و redelivery ممكن يكرروا؛ لازم deduplication أو effect . |
| Exactly-once ضمن نطاق | ممكن بضمانات محددة، زي Kafka transactions بين Kafka records و offsets؛ مش ممتد تلقائياً لـ أو payment . |
تعيين status ثابت ممكن يبقى idempotent، لكنه مش يحل stale event رجّع order من Shipped لـ Paid. استخدم sequence أو version لكل ، وارفض gap أو اعمل buffer أو reconciliation حسب العقد.
في RabbitMQ، queue واحدة و single active consumer ومعالجة sequential تقلل إعادة الترتيب؛ requeue و retries والـ multi-producer محتاجين قواعد إضافية. في Kafka الترتيب داخل partition، مش بين partitions. لو هتضمن ترتيب per order، ماتسمحش لـ row أحدث تتخطى row فاشلة لنفس order. مفيش global order مجاني.
#Sagas: choreography و orchestrationSagas: choreography and orchestration
حجز مخزون وتحصيل دفع في services مستقلة مش محلية واحدة. Saga بتتبع سلسلة local transactions، ومع الفشل بتطلب compensations زي release و refund. Compensation فعل business جديد، مش rollback سحري ولا مسح لحقيقة الدفع.
| النمط | التنسيق والمقابل |
|---|---|
| Choreography | كل service بترد على event؛ مناسب لتدفقات قصيرة، بس الـ flow بيتوزع وممكن تحصل cycles. |
| Orchestration | منسق بيخزّن state ويبعت commands ويستقبل النتائج؛ flow أوضح، لكنه مكوّن محتاج persistence و recovery. |
Pending -> StockReserved -> PaymentCaptured -> Confirmed // (1)
StockReserved + PaymentRejected -> ReleasingStock -> Cancelled
PaymentCaptured + FulfilmentFailed -> Refunding -> Refunded // (2)
- (1) خزّن transition و outgoing command بـ ، مع correlation ID و timeout لكل خطوة.
- (2) refund ممكن يفشل؛ retry محدود، ومراقبة وتعويض يدوي عند اللزوم. كل action و compensation لازم يتحملوا التكرار.
لو لازم تتحقق فوراً، فكّر تخلّيها جوه boundary واحدة بدل إدخال saga. Saga مابتوفرش ؛ pending states وحجوزات مؤقتة بتحمي من سباقات العمليات.
#Resilience و messaging frameworksResilience and messaging frameworks
خدمة بطيئة ممكن تستهلك كل connections و threads المتاحة. Retry بعشوائية jitter يعالج failure مؤقت، و timeout يحد الانتظار، و circuit breaker يوقف النداء مؤقتاً بعد فشل متكرر، و bulkhead يعزل حصة concurrency لكل dependency. ماتعيدش validation failure، وماتكررّش charge غير آمن من غير contract.
// Package: Microsoft.Extensions.Http.Resilience; Polly v8-based integration.
using Microsoft.Extensions.Http.Resilience;
builder.Services.AddHttpClient("inventory")
.AddStandardResilienceHandler(options =>
{
options.Retry.MaxRetryAttempts = 3;
options.Retry.UseJitter = true; // (1)
options.Retry.DisableForUnsafeHttpMethods(); // (2)
options.AttemptTimeout.Timeout = TimeSpan.FromSeconds(2);
options.TotalRequestTimeout.Timeout = TimeSpan.FromSeconds(10); // (3)
});
- (1) الـ handler القياسي بيجمع rate limiter للـ concurrency و total timeout و retry و circuit breaker و attempt timeout. jitter يوزع المحاولات.
- (2) تعطيل retries للـ unsafe methods سياسة محافظة؛ افهم دلالة العملية قبل تخصيصها.
- (3) ميزانية إجمالية تشمل المحاولات والتأخير؛ timeout مش إثبات إن الطرف التاني ما نفّذش.
| قديم | أنسب في الحديث |
|---|---|
| Polly v7 policy wrappers أو retries يدوية | Microsoft.Extensions.Http.Resilience المبني على Polly v8 pipelines |
MassTransit و Wolverine بيضيفوا consumer pipelines و retries و durable messaging و saga support حسب التكامل المختار. لازم تضبط persistence والـ والـ error handling صراحة؛ in-memory outbox مش بديل durable . HTTP handler ده لحماية outgoing HTTP، مش consumer retry policy.
أسئلة الانترفيو
مفيش نتايج
راجع فهمك
ليه الاختيار ده مناسب؟ إمتى ممكن ماينفعش؟ وإيه اللي هيتغير لو الحمل زاد أو ظهر فشل؟
17.1Why use asynchronous messaging instead of an HTTP call?Junior
افصل العمل اللي ينفع يتأخر، وامتص spikes، لكن اعترف بتكلفة consistency والتكرار. لو القرار مطلوب قبل الرد، ممكن يكون الاختيار الصح.
I use messaging when work can happen after the initiating , such as sending an order email. A durable queue separates service availability and buffers bursts so consumers can process at a sustainable rate. The trade-offs are , duplicate delivery and more complex operations. I keep synchronous calls when the caller needs an immediate decision, and expose a meaningful pending state when processing continues asynchronously. I also monitor message age rather than assuming an accepted message has completed.
17.2What is a message broker?Junior
وسيط بيستقبل ويوجّه ويسلّم رسائل حسب إعدادات التخزين والـ acknowledgment. مش مجرد proxy، ومش كل إعداد فيه يضمن durability.
I describe a as infrastructure that accepts messages from producers and delivers them to consumers using routing, storage and acknowledgment rules. It lets the sender and receiver operate independently in time and can buffer temporary load differences. The guarantees depend on configuration, including persistence, and confirmations. I still need explicit message contracts, monitoring and failure handling in the application. A broker acceptance signal alone does not tell me whether the consumer successfully applied the business operation.
17.3What is the difference between a command and an event?Junior
Command طلب فعل، و event حقيقة حصلت. event اسمها في الماضي زي OrderPlaced، وممكن أكتر من subscriber يستفيد بيها.
I use a command to request an action from a responsible owner, such as ReserveStock, and an event to announce a fact, such as OrderPlaced. An event can have several independent subscribers, while a command normally has one logical handler even when workers compete for it. This distinction keeps ownership clear and prevents publishers from depending on subscriber internals. I version contracts carefully and avoid including sensitive or unnecessary data simply because messaging makes distribution easy.
17.4What are RabbitMQ's main components?Mid
Producer ينشر لـ exchange، و bindings توجه لـ queues، و consumers تعالج وتعمل ack. connection فيها channels، و virtual host بيعزل topology والصلاحيات.
I publish through a channel to an exchange with a routing key. Bindings describe which queues receive the message, and consumers receive deliveries from those queues. A connection is a long-lived network connection containing lighter channels, while a virtual host provides a logical boundary for topology and permissions. I distinguish routing from storage: the exchange routes and the queue holds pending messages. A dead-letter exchange also needs an appropriate binding and queue if failures must be retained for investigation.
17.5Explain direct, topic, fanout, headers and default exchanges.Mid
Direct تطابق حرفي، و topic pattern بنقط، و fanout لكل queue، و headers حسب attributes. الـ default اسمه فاضي وبيربط queues باسمها تلقائياً.
I choose a direct exchange for exact routing-key matches and a topic exchange for dot-separated patterns, where star matches one word and hash matches zero or more. Fanout sends a copy to every bound queue and ignores the key. Headers exchanges evaluate attributes using all or any matching. The default exchange has an empty name and automatically binds each queue using its queue name. These routing choices determine recipients; competing consumers on one queue still share that queue's deliveries.
17.6How do consumer acknowledgments differ from publisher confirms?Mid
Confirm من للناشر عن قبول الرسالة. Ack من consumer للـ broker بعد المعالجة. بينهم مراحل وفشل محتمل، ومفيش واحد بيغني عن التاني.
I use publisher confirms to know that the accepted responsibility for a published message under its configured durability rules. Consumer acknowledgments report successful handling of a delivery and let the broker remove it from the queue. They protect different stages and neither replaces the other. I acknowledge only after committing the business effect. If the consumer crashes between commit and acknowledgment, the message can return, so I design for duplicates even when every publisher uses confirmations.
17.7What do prefetch, nack and a DLQ do?Mid
Prefetch يحد unacked deliveries. Nack يرفض برسالة ممكن ترجع أو تروح لو متظبط. محتاجة monitoring و replay مش مخزن تنسى فيه الأخطاء.
I tune prefetch to bound unacknowledged deliveries and balance , memory and fair distribution. A negative acknowledgment rejects a delivery, either requeuing it or allowing dead lettering when configured. I avoid unlimited immediate requeue because a poison message can create a tight failure loop. Instead, I use bounded delayed retries for transient failures and a dead-letter queue for permanent failures. I monitor both backlog age and dead-letter growth, with an explicit investigation and replay process.
17.8How do Kafka partitions and consumer groups work?Mid
Topic متقسمة logs مترتبة. داخل group التقليدية partition لها consumer نشط واحد؛ groups مختلفة offsets مستقلة. مفيش ترتيب بين partitions.
I think of a Kafka topic as multiple ordered partition logs. In a conventional consumer group, each partition is assigned to one active consumer, so partition count bounds parallel consumers in that group. Other groups read independently with their own offsets. I use a stable entity key to keep related records together and preserve per-entity order, while accounting for partition-count changes. Reading does not remove records; retention determines whether a consumer can later replay them.
17.9When would you choose RabbitMQ, Kafka or Azure Service Bus?Mid
RabbitMQ للتوجيه والـ work queues، و Kafka للـ retained streaming والـ replay، و Service Bus للـ managed enterprise messaging في Azure. القرار حسب المتطلبات والتشغيل.
I start with the consumption model and operational requirements. RabbitMQ queues fit commands, work distribution and flexible routing. Kafka fits retained event streams, independent readers and replay. Azure Service Bus fits managed enterprise messaging with queues, subscriptions, dead lettering and sessions in Azure. I compare ordering scope, recovery, retention, cost and team skills before choosing. I also specify the exact product features under discussion, since RabbitMQ Streams and extensions can change a simplistic comparison.
17.10What problem does the transactional outbox solve?Mid
Dual write بين و . الـ event يتسجل مع الداتا في واحدة، وبعدها dispatcher ينشر. التكرار لسه ممكن ومحتاج consumer .
I use an to avoid the gap between committing business data and publishing its event. The application writes both the business change and an outbox row in the same database . A dispatcher later publishes with confirmation and records success. A crash after publishing but before recording success can cause another publication, so the message ID stays stable and consumers handle duplicates. I also plan safe row claiming, retries, ordering where required, and monitoring of the oldest unsent record.
17.11How do you implement an idempotent consumer safely?Senior
Unique consumer و message ID في inbox، مع effect في نفس . Check قبل insert مش كفاية مع concurrent deliveries، والخدمات الخارجية محتاجة key خاص بيها.
I enforce a unique consumer-and-message key in an inbox and commit that record with the database effect in one . The unique constraint handles concurrent duplicates; a preliminary existence check alone is insufficient. I acknowledge only after commit, and distinguish the expected duplicate-key case from other database failures. External effects need their own contract or another durable delivery step, because a transaction cannot roll back a payment . I retain deduplication records for the supported replay window.
17.12Can you guarantee exactly-once processing?Senior
حدد النطاق الأول. Kafka ليها ضمانات داخل Kafka؛ مش معناها دفع خارجي مرة واحدة. غالباً at-least-once مع inbox و effects.
I first define the boundary of the guarantee. A platform can provide exactly-once semantics within a specific scope, such as Kafka records and offsets, without covering a separate database or external payment provider. For a cross-system workflow, I usually combine at-least-once delivery with atomic inbox processing, unique business keys and provider keys. I describe the remaining failure windows explicitly and add reconciliation, rather than claiming that a option makes every business effect happen exactly once.
17.13How do you maintain message ordering?Senior
حدد per entity ولا global. نفس key في Kafka، أو single active consumer ومعالجة sequential في RabbitMQ، مع منع retries من تخطي النسخ الفاشلة و version checks.
I define ordering per business entity whenever possible, since global ordering limits concurrency. In Kafka I use a stable entity key and sequential processing within its partition. In RabbitMQ I can use a single active consumer with sequential handling, while designing retries and redeliveries carefully. The must preserve the same stream order so later events cannot overtake a failed earlier event. Consumers track versions or sequences and have an explicit policy for stale records and gaps.
17.14What is a saga, and how do choreography and orchestration differ?Senior
Saga بتتبع local وتعويضات. Choreography أحداث موزعة، و orchestration منسق stateful. التعويض نفسه ممكن يفشل، ومفيش تلقائي.
I use a saga for a long-running workflow across boundaries, with local commits and compensating actions. Choreography lets participants react to events, while orchestration uses a coordinator to persist progress and issue commands. I prefer explicit orchestration when timeouts and recovery make the flow difficult to understand through events alone. Compensations are business actions that can fail and need and monitoring. A saga also does not provide , so reservations and pending states remain important.
17.15How do retry, timeout, circuit breaker and bulkhead work together?Mid
Retry للمؤقت بميزانية و jitter، و timeout لكل attempt وللإجمالي. Breaker يوقف النداءات فترة، و bulkhead يحد concurrency. راقب multiplication لو كل طبقة بتعيد.
I give the operation a total time budget, bound each attempt, and retry only transient failures where repeating the effect is safe. Exponential backoff with jitter prevents synchronized retry bursts. A circuit breaker rejects calls temporarily when a dependency is failing, then admits probes during recovery. A bulkhead limits concurrent work so one dependency cannot consume all capacity. In I can configure the standard resilience handler, while avoiding layered retry multiplication and treating consumer retries as a separate policy.
اختبر نفسك
إجابات الاختبار وعلامات إكمال المحاور بتتحفظ على المتصفح ده، ومش بتتنقل لأجهزة تانية.
A consumer commits a change and crashes before ack. What should you expect?
Which topic binding matches order.paid.eg?
A fanout exchange sends a copy to...
What does an outbox atomically save with the order?
Six partitions in a conventional Kafka group allow how many active partition owners?
A RabbitMQ nack with requeue=false retains the failure when...
A payment request times out. Which statement is correct?
What is a saga compensation?
Which exchange uses format=pdf and region=eg attributes for routing?
Which signal proves that the consumer completed its business effect?