AWS EventBridge: Sunucusuz Olay Veriyolu ve İçerik Tabanlı Yönlendirme
Amazon EventBridge, AWS'nin sunucusuz olay veriyoludur: olayları, içerik Lambda, SQS, Step Functions, API hedefleri ve düzinelerce başka hedefe doğru. SNS'den farklı olarak tüm abonelere yayınlanmaz ancak geçerlidir akıllı yönlendirme kuralları yük alanlarına göre. Entegre Şema Kaydı ile şema hataları tespit edilir yayın zamanında, çalışma zamanında değil.
EventBridge Mimarisi: Veri Yolu, Kural, Hedef
EventBridge üç temel konsept halinde düzenlenmiştir:
- Etkinlik Otobüsü: olayları alan kanal. Her AWS hesabının bir varsayılan olay veriyolu (EC2, S3, RDS gibi AWS hizmetlerinden olaylar alır), birden fazla özel veri yolu (özel etkinlik otobüsleri) uygulama etkinlikleri için, e iş ortağı etkinlik otobüsü SaaS entegrasyonları için (Zendesk, Datadog, Salesforce).
- Etkinlik Kuralı: bir kural olay modeli (JSON filtresi) belirleyen hangi olayların hangi hedeflere yönlendirileceği. Tek bir kuralın en fazla 5 hedefi olabilir.
- Hedef: Etkinliğin hedefi. Lambda, SQS, SNS, Step Fonksiyonlarını destekler, Diğer hesaplardan API Gateway, Kinesis, EventBridge Bus ve 20'den fazla hedef.
EventBridge Etkinliğinin Yapısı
{
"version": "0",
"id": "12345678-1234-1234-1234-123456789012",
"source": "com.mioapp.ordini",
"account": "123456789012",
"time": "2026-03-20T10:30:00Z",
"region": "eu-west-1",
"detail-type": "OrdineEffettuato",
"detail": {
"ordineId": "ord-abc123",
"clienteId": "cli-xyz789",
"totale": 149.99,
"stato": "IN_ATTESA_PAGAMENTO",
"categoria": "ELETTRONICA"
}
}
Alan detail uygulama yükünü içerir. source e detail-type olayın türünü belirleyin.
Etkinlikleri EventBridge'de Yayınlayın
// EventBridgePublisher.java - Pubblica eventi custom su EventBridge
import software.amazon.awssdk.services.eventbridge.EventBridgeClient;
import software.amazon.awssdk.services.eventbridge.model.*;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.util.*;
public class EventBridgePublisher {
private final EventBridgeClient eventBridgeClient;
private final ObjectMapper objectMapper;
private static final String EVENT_BUS_NAME = "mioapp-production";
public EventBridgePublisher() {
this.eventBridgeClient = EventBridgeClient.builder()
.region(Region.EU_WEST_1)
.build();
this.objectMapper = new ObjectMapper();
}
public void publishOrdineEffettuato(OrdineEffettuatoEvent event) throws Exception {
String detailJson = objectMapper.writeValueAsString(event);
PutEventsRequestEntry entry = PutEventsRequestEntry.builder()
.source("com.mioapp.ordini")
.detailType("OrdineEffettuato")
.detail(detailJson)
.eventBusName(EVENT_BUS_NAME)
// TraceHeader per X-Ray distributed tracing (opzionale)
.traceHeader("Root=1-63441c4a-abcdef")
.build();
PutEventsResponse response = eventBridgeClient.putEvents(
PutEventsRequest.builder()
.entries(entry)
.build()
);
if (response.failedEntryCount() > 0) {
response.entries().forEach(e -> {
if (e.errorCode() != null) {
throw new EventPublishException(
"Errore pubblicazione evento: " + e.errorCode() + " - " + e.errorMessage()
);
}
});
}
}
// Batch publish (max 10 eventi per chiamata, 256 KB totale)
public void publishBatch(List<DomainEvent> events) throws Exception {
List<PutEventsRequestEntry> entries = new ArrayList<>();
for (DomainEvent event : events) {
entries.add(PutEventsRequestEntry.builder()
.source("com.mioapp." + event.getAggregateType())
.detailType(event.getClass().getSimpleName())
.detail(objectMapper.writeValueAsString(event))
.eventBusName(EVENT_BUS_NAME)
.build());
}
PutEventsResponse response = eventBridgeClient.putEvents(
PutEventsRequest.builder()
.entries(entries)
.build()
);
System.out.printf("Pubblicati %d/%d eventi. Falliti: %d%n",
entries.size() - response.failedEntryCount(),
entries.size(),
response.failedEntryCount());
}
}
Etkinlik Modelleri: İçeriğe Dayalı Yönlendirme
EventBridge'in kalbi, içerik tabanlı yönlendirme: kurallar filtresi olaylar yalnızca olay türüne değil, JSON yükünün içeriğine de bağlıdır. Desenler önekler, sonekler, belirli değerler, sayısal aralıklar ve olumsuz koşullarla eşleşmeyi destekler.
# Esempi di Event Pattern per il routing
# Pattern 1: Tutti gli ordini di importo alto (sopra 500 EUR)
{
"source": ["com.mioapp.ordini"],
"detail-type": ["OrdineEffettuato"],
"detail": {
"totale": [{ "numeric": [">", 500] }]
}
}
# Pattern 2: Ordini con prodotti della categoria ELETTRONICA o GAMING
{
"source": ["com.mioapp.ordini"],
"detail-type": ["OrdineEffettuato"],
"detail": {
"categoria": ["ELETTRONICA", "GAMING"]
}
}
# Pattern 3: Ordini di clienti premium con pagamento completato
{
"source": ["com.mioapp.ordini"],
"detail-type": ["OrdineEffettuato", "PagamentoConfermato"],
"detail": {
"tipoCliente": ["PREMIUM", "VIP"],
"stato": [{ "prefix": "PAGA" }]
}
}
# Pattern 4: Qualsiasi evento di errore da tutti i servizi dell'app
{
"source": [{ "prefix": "com.mioapp." }],
"detail-type": [{ "suffix": "Failed" }]
}
# Pattern 5: Esclusione (not): tutti gli ordini ECCETTO i test
{
"source": ["com.mioapp.ordini"],
"detail-type": ["OrdineEffettuato"],
"detail": {
"ambiente": [{ "anything-but": ["test", "staging"] }]
}
}
CloudFormation / Terraform ile Kural Oluşturun
# Terraform: event bus, rule e target Lambda
# Custom Event Bus
resource "aws_cloudwatch_event_bus" "mioapp" {
name = "mioapp-production"
}
# Rule: ordini ad alto valore verso Lambda di VIP handling
resource "aws_cloudwatch_event_rule" "ordini_vip" {
name = "ordini-vip-handler"
event_bus_name = aws_cloudwatch_event_bus.mioapp.name
event_pattern = jsonencode({
source = ["com.mioapp.ordini"]
detail-type = ["OrdineEffettuato"]
detail = {
totale = [{ numeric = [">", 500] }]
tipoCliente = ["PREMIUM", "VIP"]
}
})
description = "Instrada ordini VIP alto valore alla Lambda dedicata"
}
# Target: Lambda VIP handler
resource "aws_cloudwatch_event_target" "ordini_vip_lambda" {
rule = aws_cloudwatch_event_rule.ordini_vip.name
event_bus_name = aws_cloudwatch_event_bus.mioapp.name
arn = aws_lambda_function.vip_handler.arn
# Retry policy per il target
retry_policy {
maximum_event_age_in_seconds = 3600 # 1 ora
maximum_retry_attempts = 3
}
# Dead letter queue per gli eventi che non vengono consegnati
dead_letter_config {
arn = aws_sqs_queue.eventbridge_dlq.arn
}
}
# Rule: tutti gli errori verso SQS per analisi
resource "aws_cloudwatch_event_rule" "tutti_errori" {
name = "tutti-errori-sqs"
event_bus_name = aws_cloudwatch_event_bus.mioapp.name
event_pattern = jsonencode({
source = [{ prefix = "com.mioapp." }]
detail-type = [{ suffix = "Failed" }]
})
}
resource "aws_cloudwatch_event_target" "errori_sqs" {
rule = aws_cloudwatch_event_rule.tutti_errori.name
event_bus_name = aws_cloudwatch_event_bus.mioapp.name
arn = aws_sqs_queue.errori_queue.arn
}
Şema Kaydı ve Şema Keşfi
EventBridge bir tane içerir Şema Kaydı yerleşik: otomatik olarak keşfedebilir otobüste yaşanan olayların kalıpları (keşif şeması) ve tanımları sürdürün Doğrulama ve kod üretimi için şema.
Başlıca avantajı, kod üretimi: keşfedilen şemadan başlayarak, EventBridge, olay yüküne karşılık gelen Java, TypeScript veya Python sınıflarını otomatik olarak oluşturur.
# Abilitare schema discovery su un event bus (AWS CLI)
aws schemas create-discoverer \
--source-arn arn:aws:events:eu-west-1:123456789012:event-bus/mioapp-production \
--description "Auto-discovery per mioapp-production"
# Elencare gli schemi scoperti
aws schemas list-schemas \
--registry-name discovered-schemas
# Scaricare il codice generato per Java
aws schemas get-code-binding-source \
--registry-name discovered-schemas \
--schema-name "com.mioapp.ordini@OrdineEffettuato" \
--language "java8" \
--schema-version "1" \
--output text > OrdineEffettuatoEvent.java
Etkinlik Arşivi ve Tekrarı
EventBridge'in en güçlü özelliklerinden biriEtkinlik Arşivi: yapılandırılabilir bir süre boyunca otobüste geçen tüm olayları otomatik olarak arşivler, izin vermek tekrar oynatma geçmiş olaylar (projeksiyon yeniden oluşturmaları için kullanışlıdır, üretim problemlerinde hata ayıklamak veya yeni tüketicileri test etmek).
# Creare un archivio per il bus degli ordini
aws events create-archive \
--archive-name mioapp-ordini-archive \
--event-source-arn arn:aws:events:eu-west-1:123456789012:event-bus/mioapp-production \
--retention-days 90 \
--event-pattern '{
"source": ["com.mioapp.ordini"],
"detail-type": ["OrdineEffettuato", "PagamentoConfermato"]
}'
# Replay degli eventi archiviati (utile per rebuild di read model)
aws events start-replay \
--replay-name rebuild-read-model-20260320 \
--event-source-arn arn:aws:events:eu-west-1:123456789012:archive/mioapp-ordini-archive \
--event-start-time "2026-01-01T00:00:00Z" \
--event-end-time "2026-03-20T23:59:59Z" \
--destination '{
"Arn": "arn:aws:events:eu-west-1:123456789012:event-bus/mioapp-production",
"FilterArns": [
"arn:aws:events:eu-west-1:123456789012:rule/mioapp-production/ordini-vip-handler"
]
}'
# Monitorare il replay
aws events describe-replay \
--replay-name rebuild-read-model-20260320
EventBridge Lambda Tüketicisi
// Handler Lambda Java per eventi EventBridge
import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.RequestHandler;
import com.amazonaws.services.lambda.runtime.events.ScheduledEvent;
import com.fasterxml.jackson.databind.ObjectMapper;
public class OrdiniVipHandler implements RequestHandler<ScheduledEvent, String> {
private final ObjectMapper objectMapper = new ObjectMapper();
private final VipNotificationService notificationService = new VipNotificationService();
@Override
public String handleRequest(ScheduledEvent event, Context context) {
context.getLogger().log("Evento ricevuto: " + event.getDetailType());
try {
// Deserializza il detail field
OrdineEffettuatoEvent ordine = objectMapper.convertValue(
event.getDetail(),
OrdineEffettuatoEvent.class
);
context.getLogger().log(String.format(
"Ordine VIP: %s, cliente: %s, totale: %.2f",
ordine.getOrdineId(),
ordine.getClienteId(),
ordine.getTotale()
));
// Invia notifica personalizzata al cliente VIP
notificationService.sendVipOrderConfirmation(
ordine.getClienteId(),
ordine.getOrdineId(),
ordine.getTotale()
);
return "SUCCESS";
} catch (Exception e) {
context.getLogger().log("ERRORE: " + e.getMessage());
// Rilancia per triggherare il retry di EventBridge
throw new RuntimeException("Elaborazione fallita", e);
}
}
}
// TypeScript handler per Lambda Node.js
// export const handler = async (event: EventBridgeEvent<'OrdineEffettuato', OrdineDetail>) => {
// const { ordineId, clienteId, totale } = event.detail;
// await sendVipNotification(clienteId, ordineId, totale);
// return { statusCode: 200 };
// };
EventBridge için En İyi Uygulamalar
- Her ortam için Özel Olay Veriyolu: Prodüksiyon, hazırlama ve geliştirme için ayrı otobüsler kullanın. Test olaylarını üretim veriyoluna göndermeyin.
- Hedefler için her zaman DLQ'yu kullanın: Her hedef için bir SQS teslim edilemeyen ileti kuyruğu yapılandırın Tüketici arızası durumunda etkinlikleri kaçırmamak için.
- Lambda tüketicilerinde iktidarsızlık: EventBridge en az bir kez teslimatı garanti eder. Lambda aynı olayın yinelenen alımını yönetmelidir.
-
Etkinlik sürümü oluşturma: Her zaman bir alan ekleyin
schemaVersionayrıntıda. EventBridge'in yerleşik bir sürüm oluşturma mekanizması yoktur: bunu veri yükünde işleyin. - Her otobüs üretimi için Etkinlik Arşivi: her zaman saklama içeren bir arşiv yapılandırın en az 30 gün. Tekrar oynatma, tüketicilerde hata olması durumunda günü kurtarabilir.
Serideki Sonraki Adımlar
- Madde 7 – SQS, SNS ve EventBridge karşılaştırması: seçim için karar kılavuzu her özel kullanım durumu için doğru AWS mesajlaşma hizmeti.
- Madde 8 – Teslim Edilmeyen Mektup Sırası ve Esneklik: Doğru şekilde yapılandırın Başarısız mesajları veri kaybı olmadan işlemek için EventBridge, SQS ve Lambda için DLQ.
Diğer Serilerle Bağlantı
- Destan Deseni (Madde 5): EventBridge ideal mesaj veriyoludur AWS'de Koreografi Efsanesi için: Her hizmet, EventBridge'e ve diğerlerine etkinlikler yayınlar hizmetler yapılandırılmış yönlendirme kuralları aracılığıyla tepki verir.
- Apaçi Kafka (Seri 38): şirket içi veya hibrit sistemler için Kafka ve EventBridge bir arada var olabilir: Yüksek verimli dahili mesajlaşma için Kafka, Harici AWS ve SaaS hizmetleriyle entegrasyon için EventBridge.







