Her SaaS’ın bir yerinde “async job” ihtiyacı ortaya çıkıyor. Email gönderme, image processing, third-party API call’ları, scheduled task’lar. Synchronous request içinde yapmak kullanıcıyı bekletiyor, bazen timeout’a sebep oluyor.
Çözüm: message queue. Async worker’lar queue’dan job’u çekip arka planda işliyor. Ama hangi queue?
3 popüler seçenek var: Redis, RabbitMQ, AWS SQS. Her birinin kullandığım projelerden notlar.
Redis queue (Redis List ya da Stream)
Redis’i queue olarak kullanabiliyorsun. LIST veri tipiyle basit queue, Stream veri tipiyle daha sophisticated.
Kullanım:
# Producer
LPUSH email_queue "{user_id: 123, template: welcome}"
# Consumer
BRPOP email_queue 0 # Blocking, beklerAvantajlar:
- Hız: microsecond latency. In-memory.
- Basit: LIST yeterli çoğu senaryo için.
- Mevcut infrastructure: Zaten cache için Redis var, queue için ek hizmet yok.
- Persistence mümkün: AOF veya RDB ile queue’ları durabilir yapabilirsin.
Dezavantajlar:
- No native retry logic. Worker fail ederse job kayboluyor. Manuel retry mantığı yazmak gerekiyor.
- Dead letter queue manuel. Failed message’ları ayrı bir queue’ya taşımak kendi işin.
- Consumer sayısı artınca dağıtım zor. Multiple consumer’a distribute için Redis Streams lazım (daha karmaşık).
- No message priority. Default LIST FIFO, priority için custom logic.
- Persistence riski: Redis kernel restart’ta AOF’ta olmayan mesaj kayboluyor.
RabbitMQ
Dedicated message broker. AMQP protokolü. 15+ yıllık mature tool.
Kullanım:
# Producer
channel.basic_publish(exchange='', routing_key='email_queue', body=message)
# Consumer
channel.basic_consume(queue='email_queue', on_message_callback=callback)
channel.basic_ack(delivery_tag) # Manual ackAvantajlar:
- Rich feature set: Priority queues, delayed messages, dead letter exchange, fanout/topic/direct exchange types.
- Acknowledgment mechanism: Worker crash olsa bile job kaybolmuyor, re-deliver ediliyor.
- Clustering support: Multi-node setup, high availability.
- Flexible routing: Topic-based routing, fanout (broadcast).
- Good monitoring: Web UI built-in. Queue depth, message rate, consumer status görünür.
Dezavantajlar:
- Operational complexity: Setup ve maintenance ciddi. Clustering karmaşık. Cluster partitioning edge case’leri.
- Memory hungry: Büyük queue’lar için RAM kullanımı yüksek.
- Network overhead: AMQP protocol verbose, Redis kadar hızlı değil.
- Team expertise: RabbitMQ tuning için deneyimli biri gerekiyor.
AWS SQS
AWS’in managed queue service’i. No ops, pay-per-use.
Kullanım:
# Producer
sqs.send_message(QueueUrl=url, MessageBody=json.dumps(data))
# Consumer
response = sqs.receive_message(QueueUrl=url, MaxNumberOfMessages=10)
for msg in response['Messages']:
process(msg)
sqs.delete_message(QueueUrl=url, ReceiptHandle=msg['ReceiptHandle'])Avantajlar:
- Fully managed: Zero ops. AWS halleder.
- Infinite scale: SQS’in nominal limit’i yok (rate limit yüksek).
- Integrated with AWS ecosystem: Lambda, SNS, Step Functions.
- Two types: Standard (at-least-once) ve FIFO (exactly-once).
- Dead letter queue native. Configure et, failed message’lar otomatik taşınıyor.
- Cheap: İlk 1 milyon request ücretsiz/ay. Normal kullanımda $1-10/ay.
Dezavantajlar:
- Higher latency: Network round trip AWS’e. Regular ~20-50ms.
- Vendor lock-in: AWS’e bağımlı. Başka cloud’a migration zor.
- Limited throughput per queue (FIFO): 300 msg/sec. Standard’da daha yüksek.
- No priority: FIFO yok native priority. Multiple queue + priority routing lazım.
- Pull-based: SNS+SQS ile push olabiliyor ama default pull. Polling mantığı kurmak lazım.
Karar matrisim
Redis seç:
- Queue simple (email, notification send)
- Low latency kritik (real-time job’lar)
- Low volume (1000 msg/gün altı)
- Ekibin Redis expertise’i var
- Zaten Redis kullanıyorsun (cache için)
RabbitMQ seç:
- Rich queue feature’lar gerekli (priority, delayed message, exchange routing)
- On-premise veya self-hosted cloud-agnostic
- Advanced routing (fanout, topic-based)
- Team has RabbitMQ experience
- High throughput + persistence kritik
AWS SQS seç:
- AWS’tesin zaten
- Ops overhead istenmiyor
- Pay-per-use maliyet modeli uygun
- Vendor lock-in OK (veya planlama var)
- Infinite scale gerekebilir
Pratik durum örneği
Kullandığım projelere göre seçim:
Proje 1 (küçük SaaS, 10K user):
– Email, push notification async
– Zaten Redis var
– Volume düşük
– Seçim: Redis LIST
Proje 2 (büyük SaaS, 500K user):
– Multiple worker type (email, image processing, analytics)
– Priority queue lazım
– High availability kritik
– Seçim: RabbitMQ
Proje 3 (AWS-hosted, serverless architecture):
– Lambda function’ları çalıştırıyor
– Unpredictable traffic
– Ops ekibi yok
– Seçim: AWS SQS
Her birinin doğru senaryosu var.
Common patterns
Hangi queue seçersen seç, bazı pattern’ler evrensel:
1. Idempotent job handler. Aynı mesaj 2 kere gelse de sonuç aynı olmalı. Retry ve duplicate delivery için kritik.
2. Exponential backoff retry. Fail olan job 1s, 2s, 4s… gecikmelerle retry. Infrastructure’ı stress’lemiyor.
3. Dead letter queue. N kez retry sonrası failed job’lar ayrı queue’ya. Manual review.
4. Message TTL. Old message’lar expire olsun. 7 gün sonra hâlâ process edilmemiş mesaj muhtemelen artık irrelevant.
5. Monitoring ve alerting. Queue depth, consumer lag, error rate. Bu metric’ler olmadan queue production’a göndermiyorum.
Throughput vs latency
Hangi optimize edilmeli?
Low latency (real-time): Redis. Microsecond. Email confirmation gibi hızlı işler.
High throughput: RabbitMQ veya SQS. Saniyede 10K+ message.
Balanced: SQS. Çoğu senaryo için yeterli latency + scale.
Latency milisaniyeler ise Redis. Saniyeler tolere edilebilir ise SQS. Fancy routing gerekli ise RabbitMQ.
Cost karşılaştırması
5M message/ay scenario:
- Redis: Kendi hosted. AWS ElastiCache t3.medium ~$40/ay. Ama cache için kullanıyorsan zaten var.
- RabbitMQ: AWS MQ ~$100/ay. Self-hosted EC2’de biraz ucuz ama ops overhead.
- SQS: ~$2/ay (ilk 1M ücretsiz, sonrası $0.40/1M).
SQS volume-based maliyetli olarak en ucuz. Küçük scale’de neredeyse ücretsiz.
Migration between them
Proje başladın bir queue ile. Büyüdükçe başkasına geçmen gerekiyor.
Iyi news: abstraction layer yazarsan migration kolay. Job publisher/consumer interface’i backend’le decoupled:
class JobQueue:
def publish(self, job_type, payload): ...
def consume(self, job_type, handler): ...Backend Redis, RabbitMQ, SQS – underneath’te hangisi olduğunu bilmiyor worker code.
Ben şöyle başlıyorum: interface + Redis implementation. İhtiyaç oluşunca RabbitMQ/SQS implementation ekliyorum. Config’le switch.
Sonuç
Message queue seçimi scaling + operational complexity + feature set’in balance’ı. Redis simple ve hızlı, RabbitMQ feature-rich, SQS managed ve AWS-native.
Küçük başlayanlar Redis’le yeterli. Volume arttıkça managed (SQS) veya feature’lı (RabbitMQ) switch mantıklı. Kararı “ne kadar işim büyüyecek” tahmininle değil, şu anki somut ihtiyaçla ver. Abstraction layer’la migration’ı kolaylaştır.