Message Production
Message Production
Producers create and send messages to message queues or topics.
Production Flow
Producer Flow:
1. Create message (payload + metadata)
2. Serialize message
3. Select destination (topic/queue)
4. Send message
5. Handle acknowledgment/error
Kafka Producer Example
Properties props = new Properties();
props.put("bootstrap.servers", "kafka:9092");
props.put("key.serializer", "org.apache.kafka.common.serialization.StringSerializer");
props.put("value.serializer", "org.apache.kafka.common.serialization.StringSerializer");
props.put("acks", "all");
props.put("retries", 3);
KafkaProducer<String, String> producer = new KafkaProducer<>(props);
// Send message
ProducerRecord<String, String> record = new ProducerRecord<>(
"orders", // topic
"order-123", // key
orderJson // value
);
producer.send(record, (metadata, exception) -> {
if (exception != null) {
log.error("Failed to send message", exception);
} else {
log.info("Message sent to partition {} offset {}",
metadata.partition(), metadata.offset());
}
});
RabbitMQ Producer Example
import pika
import json
connection = pika.BlockingConnection(
pika.ConnectionParameters('localhost')
)
channel = connection.channel()
# Publish message
channel.basic_publish(
exchange='orders',
routing_key='order.created',
body=json.dumps({
'order_id': '123',
'user_id': '456',
'amount': 99.99
}),
properties=pika.BasicProperties(
delivery_mode=2, # Persistent
content_type='application/json'
)
)
Production Patterns
| Pattern | Description | Use Case |
|---|---|---|
| Fire-and-forget | No acknowledgment | Logs, metrics |
| Sync publish | Wait for ack | Critical data |
| Async publish | Callback on ack | High throughput |
| Batch publish | Group messages | Bulk operations |
Serialization
Message Serialization
Serialization Formats
Comparison:
Format | Size | Speed | Schema | Human Readable
------------|---------|---------|---------|----------------
JSON | Large | Slow | No | Yes
Avro | Small | Fast | Yes | No
Protobuf | Small | Fast | Yes | No
MessagePack | Medium | Fast | No | No
Thrift | Small | Fast | Yes | No
JSON Serialization
import json
# Serialize
data = {'order_id': 123, 'items': ['a', 'b']}
message = json.dumps(data)
# Deserialize
parsed = json.loads(message)
Avro Serialization
import avro.schema
from io import BytesIO
import avro.io
# Schema
schema = avro.schema.parse('''
{
"type": "record",
"name": "Order",
"fields": [
{"name": "order_id", "type": "int"},
{"name": "amount", "type": "float"}
]
}
''')
# Serialize
writer = avro.io.DatumWriter(schema)
buffer = BytesIO()
encoder = avro.io.Encoder(buffer)
writer.write(data, encoder)
message = buffer.getvalue()
# Deserialize
reader = avro.io.DatumReader(schema)
buffer = BytesIO(message)
decoder = avro.io.Decoder(buffer)
parsed = reader.read(decoder)
Protobuf Serialization
// order.proto
syntax = "proto3";
message Order {
int32 order_id = 1;
float amount = 2;
repeated string items = 3;
}
# Python usage
import order_pb2
# Serialize
order = order_pb2.Order()
order.order_id = 123
order.amount = 99.99
message = order.SerializeToString()
# Deserialize
parsed = order_pb2.Order()
parsed.ParseFromString(message)
When to Use Each
| Format | When to Use |
|---|---|
| JSON | Prototyping, human debugging, simple schemas |
| Avro | Kafka, schema evolution, compact size |
| Protobuf | gRPC, strict schemas, performance |
| MessagePack | Alternative to JSON, better performance |
Error Handling
Producer Error Handling
Error Types
1. Network Errors:
- Connection timeout
- DNS resolution failure
- Network partition
2. Broker Errors:
- Leader not available
- Not enough replicas
- Request timeout
3. Serialization Errors:
- Schema mismatch
- Invalid data format
4. Configuration Errors:
- Invalid topic
- Invalid partition
Retry Strategy
class ResilientProducer:
def __init__(self, producer, max_retries=3, retry_delay=1.0):
self.producer = producer
self.max_retries = max_retries
self.retry_delay = retry_delay
def send(self, topic, key, value):
for attempt in range(self.max_retries):
try:
future = self.producer.send(topic, key, value)
record_metadata = future.get(timeout=10)
return record_metadata
except KafkaError as e:
if attempt < self.max_retries - 1:
time.sleep(self.retry_delay * (2 ** attempt)) # Exponential backoff
else:
raise e
Idempotent Producer
// Kafka idempotent producer
Properties props = new Properties();
props.put("enable.idempotence", "true");
props.put("acks", "all");
props.put("retries", Integer.MAX_VALUE);
props.put("max.in.flight.requests.per.connection", 5);
// Guarantees:
// - Exactly-once delivery per partition
// - Messages in order within partition
Dead Letter Queue
def send_with_dlq(producer, topic, dlq_topic, message):
try:
producer.send(topic, message)
except Exception as e:
# Send to DLQ for later processing
producer.send(dlq_topic, {
'original_topic': topic,
'message': message,
'error': str(e),
'timestamp': time.time()
})
Best Practices
- Use idempotent producers to prevent duplicates
- Implement retries with exponential backoff
- Set appropriate timeouts for send operations
- Use DLQ for failed messages
- Monitor producer metrics (send rate, error rate, latency)
Practice Problems
Design a scalable Producers system. Cover high-level architecture, data model, and API design.
Solution
// Complete system design:
// - Functional + Non-functional requirements
// - Capacity estimation
// - Data model (SQL/NoSQL choice)
// - API endpoints
// - Component architecture
// - Scaling strategy
// - Monitoring & reliabilityHow would you scale Producers to handle 10x the current load? Identify bottlenecks and solutions.
Solution
// Scaling approach:
// 1. Load balancing
// 2. Database sharding/replication
// 3. Cache layer (Redis)
// 4. CDN for static assets
// 5. Async processing (queues)
// 6. Microservices decompositionAnalyze potential failure modes for Producers and design mitigation strategies.
Solution
// Failure mitigation:
// 1. Redundancy (multi-AZ)
// 2. Circuit breakers
// 3. Retry with backoff
// 4. Dead letter queues
// 5. Health checks
// 6. Graceful degradationQuiz
1. What is the purpose of message serialization?
2. Which serialization format provides schema evolution?
3. What is an idempotent producer?
4. What is exponential backoff in retry logic?
5. Why use a Dead Letter Queue for failed messages?
Flashcards
Question
What is message serialization?
Click to reveal answer
Answer
Converting in-memory objects to byte format (JSON, Avro, Protobuf) for network transmission
Question
JSON vs Avro vs Protobuf?
Click to reveal answer
Answer
JSON: human-readable, no schema. Avro: compact, schema evolution. Protobuf: fast, strict schema.
Question
What is an idempotent producer?
Click to reveal answer
Answer
Ensures exactly-once delivery to a partition even with retries, preventing duplicate messages
Question
What is exponential backoff?
Click to reveal answer
Answer
Retry strategy where delay doubles after each attempt, reducing load on failing services
Question
Why use DLQ for failed messages?
Click to reveal answer
Answer
To store failed messages for later investigation and processing without blocking the main queue
Revision Notes
Key Takeaways
- 1.Producers create and send messages with proper serialization
- 2.Choose serialization format based on needs (JSON, Avro, Protobuf)
- 3.Idempotent producers prevent duplicate messages on retries
- 4.Exponential backoff reduces load on failing services
- 5.DLQ stores failed messages for later processing
Interview Tips
- •Compare serialization formats (JSON, Avro, Protobuf)
- •Explain idempotent producer guarantees
- •Discuss retry strategies and backoff algorithms
- •Mention DLQ as part of error handling strategy
Cheat Sheet
Cheat Sheet: Producers
Production Patterns
- Fire-and-forget: No ack
- Sync: Wait for ack
- Async: Callback on ack
- Batch: Group messages
Serialization
- JSON: Simple, human-readable
- Avro: Compact, schema evolution
- Protobuf: Fast, strict schema
Error Handling
- Retry with exponential backoff
- Idempotent producer (exactly-once)
- Dead Letter Queue for failures
- Appropriate timeouts
Best Practices
- Use idempotent producers
- Implement DLQ
- Monitor producer metrics