flowchart LR
PA["Producent A"] --> T["Temat:\nzamówienia"]
PB["Producent B"] --> T
PC["Producent C"] --> T
T --> CX["Konsument X\n(Grupa 1)"]
T --> CY["Konsument Y\n(Grupa 1)"]
T --> CZ["Konsument Z\n(Grupa 2)"]
style T fill:#FF9800,color:#fff
style PA fill:#2196F3,color:#fff
style PB fill:#2196F3,color:#fff
style PC fill:#2196F3,color:#fff
style CX fill:#4CAF50,color:#fff
style CY fill:#4CAF50,color:#fff
style CZ fill:#9C27B0,color:#fff
Wykład 4 — Apache Kafka
Analiza danych w czasie rzeczywistym
1 Od monolitu do mikroserwisów — kontekst
Zanim przejdziemy do Kafki, krótki kontekst. Tradycyjna aplikacja to monolit — jeden duży program, który robi wszystko: obsługuje użytkowników, przetwarza dane, generuje raporty. Łatwy na start, ale trudny do skalowania i modyfikowania.
Mikroserwisy to podejście, w którym aplikację dzielimy na małe, niezależne usługi — każda odpowiedzialna za jedno zadanie. Usługa „płatności” nie zna szczegółów usługi „rekomendacji”. Komunikują się przez API lub kolejki wiadomości.
Gdy masz 20 mikroserwisów, które muszą wymieniać dane, tworzysz siatkę połączeń punkt-punkt. Każdy nowy serwis wymaga integracji z wieloma innymi. To nie skaluje się.
Rozwiązanie: centralny broker wiadomości — jedno miejsce, przez które przepływają wszystkie dane. I tu wchodzi Apache Kafka.
2 Co to jest Apache Kafka?
Kafka to rozproszona platforma strumieniowa stworzona w LinkedIn (2011), obecnie rozwijana jako projekt Apache. Nie jest „kolejnym systemem kolejkowym” — to coś więcej.
Działa na wielu serwerach (brokerach) jako klaster.
Wiadomości zapisywane na dysku, nie znikają po odczytaniu.
Obsługuje miliony wiadomości na sekundę.
Dodajesz brokerów i partycji w miarę wzrostu danych.
W klasycznym systemie kolejkowym (np. RabbitMQ) wiadomość jest usuwana po przetworzeniu. Kafka tego nie robi — wiadomości są przechowywane na dysku przez konfigurowalny czas (domyślnie 7 dni). Dzięki temu:
- wielu konsumentów może odczytać te same dane,
- konsument może wrócić do wcześniejszych wiadomości (replay),
- awaria konsumenta nie powoduje utraty danych.
3 Architektura Kafki
3.1 Wzorzec Publish/Subscribe
Kafka implementuje wzorzec pub/sub (publikuj/subskrybuj). Nadawca (producent) nie wysyła wiadomości bezpośrednio do odbiorcy — publikuje ją do tematu. Odbiorcy (konsumenci) subskrybują tematy, które ich interesują.
3.2 Tematy i partycje
Temat to logiczny kanał danych — jak folder, do którego trafiają wiadomości określonego typu. Np. transakcje, zamowienia, logi-serwera, odczyty-sensorow.
Każdy temat jest podzielony na jedną lub więcej partycji. Partycja to uporządkowana, niezmienna sekwencja wiadomości — każda wiadomość otrzymuje unikalny numer (offset).
Partycje służą dwóm celom:
- Skalowalność — dane jednego tematu mogą być rozproszone na wielu brokerach.
- Równoległość — wielu konsumentów może czytać różne partycje jednocześnie.
Broker to pojedynczy serwer Kafki. Klaster Kafki składa się z wielu brokerów. Każdy broker przechowuje część partycji.
Aby zapewnić niezawodność, Kafka replikuje partycje na wielu brokerach. Współczynnik replikacji (np. 3) oznacza, że każda partycja ma 3 kopie na różnych serwerach. Jeśli jeden broker padnie — dane nie giną.
flowchart LR
subgraph "Temat: transakcje"
direction TB
P0["Partycja 0\noffset: 0,1,2,3..."]
P1["Partycja 1\noffset: 0,1,2,3..."]
P2["Partycja 2\noffset: 0,1,2,3..."]
end
P0 -.-> B1["Broker 1"]
P1 -.-> B2["Broker 2"]
P2 -.-> B3["Broker 3"]
style P0 fill:#E3F2FD,stroke:#2196F3
style P1 fill:#E3F2FD,stroke:#2196F3
style P2 fill:#E3F2FD,stroke:#2196F3
style B1 fill:#FFF3E0,stroke:#FF9800
style B2 fill:#FFF3E0,stroke:#FF9800
style B3 fill:#FFF3E0,stroke:#FF9800
4 Producenci (Producers)
Producent to aplikacja, która wysyła wiadomości do Kafki. Producent wskazuje temat, a Kafka przypisuje wiadomość do partycji:
- domyślnie: round-robin (kolejno do każdej partycji),
- z kluczem: Kafka oblicza hash klucza i na tej podstawie wybiera partycję — wiadomości z tym samym kluczem zawsze trafiają do tej samej partycji.
from kafka import KafkaProducer
import json
from datetime import datetime
producer = KafkaProducer(
bootstrap_servers='localhost:9092',
value_serializer=lambda v: json.dumps(v).encode('utf-8')
)
# Wysyłanie transakcji do tematu "transakcje"
transakcja = {
'id': 'TX0042',
'kwota': 1250.00,
'klient': 'K-1001',
'czas': datetime.now().isoformat(),
'sklep': 'Warszawa'
}
producer.send('transakcje', value=transakcja)
producer.flush()
print(f"Wysłano: {transakcja}")Jeśli chcemy, żeby wszystkie transakcje tego samego klienta trafiały do jednej partycji (np. w celu analizy sesji):
# Klucz = ID klienta → ten sam klient zawsze w tej samej partycji
producer.send(
'transakcje',
key=b'K-1001', # klucz partycjonowania
value=transakcja
)Używaj klucza, gdy potrzebujesz gwarancji kolejności dla danej encji (np. wszystkie transakcje klienta K-1001 w kolejności) lub gdy dalsze przetwarzanie wymaga zgrupowania po kluczu.
5 Konsumenci (Consumers)
Konsument to aplikacja, która odczytuje wiadomości z tematu. Konsument śledzi swój offset — wie, którą wiadomość ostatnio przetworzył. Po awarii wznawia od tego miejsca.
from kafka import KafkaConsumer
import json
consumer = KafkaConsumer(
'transakcje',
bootstrap_servers='localhost:9092',
auto_offset_reset='earliest',
value_deserializer=lambda x: json.loads(x.decode('utf-8'))
)
for message in consumer:
tx = message.value
if tx['kwota'] > 1000:
print(f"Duża transakcja: {tx['id']} = {tx['kwota']} PLN ({tx['sklep']})")5.1 Grupy konsumentów (Consumer Groups)
Gdy jeden konsument nie nadąża z przetwarzaniem — dodajesz kolejnych. Konsumenci w tej samej grupie dzielą między siebie partycje.
- Kafka automatycznie przypisuje partycje do konsumentów w grupie.
- Każdą partycję czyta dokładnie jeden konsument z grupy.
- Jeśli konsument padnie — jego partycje przejmuje inny.
- Ideał: liczba konsumentów w grupie = liczba partycji.
- Jeśli konsumentów jest więcej niż partycji — nadmiarowi będą bezczynni.
Temat: transakcje (3 partycje)
Grupa konsumentów: "fraud-detection"
Partycja 0 ──→ Konsument A
Partycja 1 ──→ Konsument B
Partycja 2 ──→ Konsument C
Różne grupy konsumentów mogą czytać te same dane niezależnie. Np. grupa fraud-detection analizuje transakcje pod kątem oszustw, a grupa reporting buduje raporty — obie czytają temat transakcje, ale mają osobne offsety.
6 Mikroserwis z modelem ML — FastAPI
W praktyce konsument Kafki często przekazuje dane do modelu ML serwowanego przez API. FastAPI to lekki framework Pythonowy idealny do tego celu.
from fastapi import FastAPI
import numpy as np
import pickle
app = FastAPI()
# Wczytanie wytrenowanego modelu
# with open("model.pkl", "rb") as f:
# model = pickle.load(f)
@app.get("/predict/")
def predict_price(area: float, bedrooms: int, age: int):
"""Prognoza ceny nieruchomości."""
features = np.array([[area, bedrooms, age]])
# price = model.predict(features)[0]
price = area * 8500 + bedrooms * 50000 - age * 2000 # uproszczony model
return {"estimated_price": round(price, 2)}
# Uruchomienie: uvicorn main:app --reload
# Zapytanie: http://localhost:8000/predict/?area=75&bedrooms=3&age=10Zapytanie (Request):
- URL:
http://localhost:8000/predict/?area=75&bedrooms=3&age=10 - Metoda HTTP: GET lub POST
- Nagłówki: Content-Type, Authorization
- Ciało (body): dane w formacie JSON
Odpowiedź (Response):
- Status HTTP:
200 OK,400 Bad Request,500 Internal Server Error - Ciało: wynik w formacie JSON
{"estimated_price": 787500.0}7 Kafka + ML — kompletny obraz
Łącząc elementy z ostatnich wykładów, oto schemat typowego systemu real-time analytics:
flowchart LR
subgraph Źródła
WEB["Aplikacja webowa"]
IOT["Sensory IoT"]
PAY["System płatności"]
end
subgraph Kafka
T["Temat:\nevents"]
end
subgraph Przetwarzanie
SP["Spark Streaming"]
API["FastAPI + ML"]
DB["Zapis do bazy"]
end
subgraph Wyniki
DASH["Dashboard"]
ALERT["Alerty"]
REP["Raporty"]
end
WEB --> T
IOT --> T
PAY --> T
T --> SP --> DASH
T --> API --> ALERT
T --> DB --> REP
style T fill:#FF9800,color:#fff
style SP fill:#2196F3,color:#fff
style API fill:#4CAF50,color:#fff
style DB fill:#9C27B0,color:#fff
Producenci (aplikacje, sensory, systemy) wysyłają zdarzenia do Kafki. Konsumenci (Spark, FastAPI, systemy raportowe) odczytują je i przetwarzają — każdy na swój sposób, niezależnie od pozostałych.
8 Podsumowanie
Kafka to centralny element architektury strumieniowej. Jej siła tkwi w trwałości danych (wiadomości nie znikają), skalowalności (partycje, replikacja) i elastyczności (wielu niezależnych konsumentów). Na laboratoriach sami uruchomicie klaster Kafki w Dockerze i napiszecie producentów i konsumentów w Pythonie.
Apache Spark i Structured Streaming — przetwarzanie danych strumieniowych na dużą skalę, integracja z Kafką.
Projektujesz system monitorowania cen konkurencji dla sieci sklepów. Jakie tematy Kafki byś utworzył? Ile partycji? Jakie grupy konsumentów?