Files
votianlt/MESSAGING_LAYER.md
2025-10-23 12:18:42 +02:00

319 lines
9.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Message Delivery Layer - Implementierungsdokumentation
## Übersicht
Der **Message Delivery Layer** ist eine neue Abstraktionsschicht zwischen der Anwendungslogik und dem MQTT-Transport. Er bietet:
-**Zuverlässige Nachrichtenzustellung** mit Bestätigungsmechanismus
-**Automatische Wiederholungsversuche** bei fehlgeschlagenen Zustellungen
-**Protokoll-Abstraktion** für einfachen Austausch von MQTT gegen andere Transporte
-**Vollständiges Audit-Log** aller Nachrichten
-**Queue-basiertes Design** für Skalierbarkeit
## Architektur
```
Application Layer (MessageController, Services)
MessageDeliveryService (Envelope-Wrapping, Queue-Management)
MessagingTransport Interface (Protokoll-Abstraktion)
MqttTransportAdapter (MQTT-spezifische Implementierung)
MqttV5ClientManager (HiveMQ Client)
```
## Komponenten
### 1. Datenmodelle (`messaging/model/`)
#### MessageEnvelope
Wrapper für alle Nachrichten mit Metadaten:
- `messageId`: Eindeutige UUID
- `timestamp`: Erstellungszeitpunkt
- `topic`: Ziel-Topic
- `payload`: Ursprüngliche Nachricht
- `requiresAck`: Bestätigung erforderlich?
- `retryCount`: Anzahl Wiederholungsversuche
- `expiresAt`: Ablaufzeitpunkt
#### PendingDelivery
Tracking-Objekt für ausstehende Zustellungen:
- `messageId`: Referenz zur Envelope
- `status`: PENDING, SENT, ACKNOWLEDGED, FAILED, EXPIRED
- `retryCount`: Aktuelle Wiederholungsversuche
- `nextRetryAt`: Zeitpunkt des nächsten Versuchs
- `envelopeData`: Serialisierte Envelope-Daten
#### AcknowledgmentMessage
Bestätigungsnachricht vom Client:
- `messageId`: ID der bestätigten Nachricht
- `status`: RECEIVED, PROCESSED, FAILED
- `clientId`: Absender der Bestätigung
- `errorMessage`: Optional bei Fehler
### 2. Transport Layer (`messaging/transport/`)
#### MessagingTransport Interface
Protokoll-agnostische Schnittstelle:
```java
CompletableFuture<Void> send(String topic, byte[] payload, TransportOptions options);
void subscribe(String topicPattern, MessageHandler handler);
boolean isConnected();
String getTransportType();
```
#### MqttTransportAdapter
MQTT-Implementierung des Transport-Interfaces:
- Adaptiert HiveMQ MQTT Client
- Unterstützt MQTT-Wildcards (+ und #)
- QoS-Mapping (0, 1, 2)
### 3. Delivery Service (`messaging/delivery/`)
#### MessageDeliveryService
Zentrale Orchestrierung:
- `sendMessage()`: Nachricht mit Tracking senden
- `handleIncomingMessage()`: Eingehende Envelope verarbeiten
- `handleAcknowledgment()`: Bestätigung verarbeiten
- `retryPendingDeliveries()`: Wiederholungsversuche durchführen
- `cleanupOldDeliveries()`: Alte Einträge aufräumen
#### AcknowledgmentHandler
Routing eingehender Nachrichten zur Anwendungslogik:
- Unwrapping der Envelope
- Routing basierend auf Topic-Pattern
- Delegation an MessageController
#### RetryScheduler
Geplante Tasks:
- Retry-Task: Alle 30 Sekunden (konfigurierbar)
- Cleanup-Task: Stündlich (konfigurierbar)
### 4. Repositories
#### MessageEnvelopeRepository
Speicherung aller Envelope-Objekte in MongoDB Collection `message_envelopes`
#### PendingDeliveryRepository
Tracking ausstehender Zustellungen in MongoDB Collection `pending_deliveries`
## Nachrichtenfluss
### Outbound (Server → Client)
1. **Anwendung** ruft `MessageDeliveryService.sendMessage()` auf
2. **MessageEnvelope** wird erstellt mit eindeutiger `messageId`
3. **PendingDelivery** wird in Datenbank gespeichert (Status: PENDING)
4. **Envelope** wird als JSON serialisiert
5. **Transport** sendet Nachricht via MQTT
6. **Status** wird auf SENT aktualisiert, `nextRetryAt` gesetzt
7. **Client** empfängt Nachricht und sendet ACK
8. **AcknowledgmentHandler** verarbeitet ACK
9. **PendingDelivery** wird auf ACKNOWLEDGED gesetzt
### Inbound (Client → Server)
1. **MQTT** empfängt Nachricht auf Topic
2. **MqttTransportAdapter** leitet an MessageDeliveryService weiter
3. **MessageEnvelope** wird aus JSON deserialisiert
4. **ACK** wird automatisch an Client gesendet (falls `requiresAck=true`)
5. **AcknowledgmentHandler** routet Payload zur Anwendung
6. **MessageController** verarbeitet Business-Logik
## Retry-Mechanismus
### Exponential Backoff
```
Versuch 1: 5 Sekunden
Versuch 2: 10 Sekunden (5s × 2.0)
Versuch 3: 20 Sekunden (10s × 2.0)
Versuch 4: 40 Sekunden (20s × 2.0)
Maximum: 5 Minuten
```
### Ablauf
1. **RetryScheduler** läuft alle 30 Sekunden
2. Findet alle `PendingDelivery` mit Status SENT und `nextRetryAt` in Vergangenheit
3. Prüft auf Ablauf (`expiresAt`) und Max-Retries
4. Sendet Nachricht erneut via Transport
5. Aktualisiert `retryCount` und `nextRetryAt`
## Konfiguration
### application.properties
```properties
# Message Delivery Layer Configuration
app.messaging.delivery.max-retries=3
app.messaging.delivery.retry-initial-delay=5s
app.messaging.delivery.retry-max-delay=5m
app.messaging.delivery.retry-backoff-multiplier=2.0
app.messaging.delivery.ack-timeout=30s
app.messaging.delivery.message-expiry=24h
app.messaging.delivery.cleanup-interval-minutes=60
app.messaging.delivery.retry-interval-seconds=30
app.messaging.delivery.acknowledged-retention-days=7
```
## MQTT Topics
### Outbound (Server → Client)
```
/client/{clientId}/message # Chat-Nachrichten (wrapped)
/client/{clientId}/jobs # Job-Zuweisungen (wrapped)
/client/{clientId}/auth # Login-Antworten (wrapped)
```
### Inbound (Client → Server)
```
/server/{clientId}/task_completed # Task-Abschlüsse (wrapped)
/server/{clientId}/message # Chat-Nachrichten (wrapped)
/server/{clientId}/jobs/assigned # Job-Anfragen (wrapped)
/server/login # Login-Anfragen (wrapped)
```
### Acknowledgments
```
/ack/server/{messageId} # Client → Server ACK
/ack/client/{clientId}/{messageId} # Server → Client ACK
```
## Migration bestehender Clients
### Phase 1: Server-seitig (✅ Abgeschlossen)
- Message Delivery Layer implementiert
- MqttPublisher nutzt jetzt MessageDeliveryService
- Backward-kompatibel: Legacy-Nachrichten werden weiterhin verarbeitet
### Phase 2: Client-seitig (TODO)
Flutter-App muss angepasst werden:
1. **Envelope-Handling**
```dart
class MessageEnvelope {
String messageId;
DateTime timestamp;
String topic;
Map<String, dynamic> payload;
bool requiresAck;
}
```
2. **Empfang**
```dart
void handleIncomingMessage(String topic, String json) {
final envelope = MessageEnvelope.fromJson(json);
// ACK senden
if (envelope.requiresAck) {
sendAcknowledgment(envelope.messageId);
}
// Payload verarbeiten
processPayload(envelope.payload);
}
```
3. **ACK senden**
```dart
void sendAcknowledgment(String messageId) {
final ack = {
'messageId': messageId,
'status': 'RECEIVED',
'timestamp': DateTime.now().toIso8601String(),
'clientId': myClientId
};
mqtt.publish('/ack/server/$messageId', jsonEncode(ack));
}
```
4. **Senden**
```dart
void sendMessage(String topic, Map<String, dynamic> payload) {
final envelope = MessageEnvelope(
messageId: Uuid().v4(),
timestamp: DateTime.now(),
topic: topic,
payload: payload,
requiresAck: true
);
mqtt.publish(topic, jsonEncode(envelope.toJson()));
}
```
## Monitoring & Debugging
### Logging
Alle Komponenten loggen mit Präfix:
- `[MessageDelivery]`: MessageDeliveryService
- `[MqttTransport]`: MqttTransportAdapter
- `[AckHandler]`: AcknowledgmentHandler
- `[RetryScheduler]`: RetryScheduler
- `[MessagingConfig]`: MessagingConfig
### Datenbank-Queries
**Ausstehende Zustellungen:**
```javascript
db.pending_deliveries.find({ status: "SENT" })
```
**Fehlgeschlagene Zustellungen:**
```javascript
db.pending_deliveries.find({ status: "FAILED" })
```
**Retry-Statistiken:**
```javascript
db.pending_deliveries.aggregate([
{ $group: { _id: "$status", count: { $sum: 1 } } }
])
```
**Nachrichten eines Clients:**
```javascript
db.pending_deliveries.find({ client_id: "client123" })
```
## Vorteile
### Zuverlässigkeit
- Garantierte Zustellung durch Retry-Mechanismus
- Persistierung in MongoDB verhindert Datenverlust
- Acknowledgment-Tracking für Nachvollziehbarkeit
### Skalierbarkeit
- Queue-basiertes Design
- Asynchrone Verarbeitung mit CompletableFuture
- Scheduled Tasks für Hintergrundverarbeitung
### Wartbarkeit
- Klare Trennung der Verantwortlichkeiten
- Protokoll-Abstraktion ermöglicht einfachen Austausch
- Umfangreiches Logging für Debugging
### Flexibilität
- Konfigurierbare Retry-Parameter
- Verschiedene DeliveryOptions (standard, fireAndForget, critical)
- Erweiterbar für andere Transporte (WebSocket, gRPC, etc.)
## Nächste Schritte
1. ✅ Server-seitige Implementierung abgeschlossen
2. ⏳ Flutter-App anpassen (Envelope-Handling)
3. ⏳ Integration-Tests schreiben
4. ⏳ Monitoring-Dashboard erstellen
5. ⏳ Performance-Optimierung
6. ⏳ Dokumentation für Client-Entwickler
## Support
Bei Fragen oder Problemen:
- Logs prüfen: `logs/votianlt.log`
- MongoDB Collections: `message_envelopes`, `pending_deliveries`
- Konfiguration: `application.properties`