Einführung in heterogene Systeme

Was sind heterogene Systeme?

Heterogene Systeme sind IT-Infrastrukturen oder Umgebungen, in denen unterschiedliche Plattformen, Technologien, Hardware oder Software zusammenarbeiten müssen, um eine Aufgabe zu erfüllen. Diese Systeme bestehen aus verschiedenen Subsystemen, die möglicherweise unterschiedliche Protokolle, Datenformate und Kommunikationsmethoden verwenden. Die Fähigkeit, effizient zu kommunizieren, ist entscheidend, um die Interoperabilität und Funktionalität eines heterogenen Systems zu gewährleisten.

Herausforderungen bei der Kommunikation zwischen heterogenen Systemen

  • Unterschiedliche Plattformen und Betriebssysteme
  • Unterschiedliche Datenformate und Protokolle
  • Unterschiedliche Sicherheitsanforderungen
  • Performance- und Latenzprobleme
  • Fehlerbehandlung in verteilten Umgebungen

Typische Anwendungsfälle

  • Integration von Altsystemen mit modernen Anwendungen
  • Kommunikation zwischen verschiedenen Cloud-Diensten und On-Premise-Systemen
  • Vernetzung von Microservices in einer verteilten Architektur

Protokolle zur Kommunikation

REST (Representational State Transfer)

REST ist ein leichtgewichtiges, auf HTTP basierendes Architekturprinzip, das in verteilten Systemen weit verbreitet ist. REST nutzt die HTTP-Methoden (GET, POST, PUT, DELETE) zur Kommunikation und verwendet meist JSON oder XML als Datenformat.

Beispiel für REST-API in Java:


@RestController
@RequestMapping("/api")
public class UserController {

    @GetMapping("/users/{id}")
    public User getUser(@PathVariable Long id) {
        return new User(id, "John Doe");
    }

    @PostMapping("/users")
    public User createUser(@RequestBody User user) {
        user.setId(1L);
        return user;
    }
}

SOAP (Simple Object Access Protocol)

SOAP ist ein auf XML basierendes Protokoll, das in verteilten Systemen oft für die Integration komplexer, transaktionaler Prozesse verwendet wird.

Beispiel für SOAP-Webservice in Java:


@WebService
public class HelloWorldService {

    @WebMethod
    public String sayHello(String name) {
        return "Hello, " + name;
    }

    public static void main(String[] args) {
        Endpoint.publish("http://localhost:8080/ws/hello", new HelloWorldService());
    }
}

gRPC (Google Remote Procedure Call)

gRPC ist ein leistungsfähiges, auf HTTP/2 basierendes RPC-Framework, das Protocol Buffers zur Serialisierung verwendet und vor allem in modernen verteilten Systemen und Microservices eingesetzt wird.

Beispiel für gRPC-Server in Java:

public class HelloWorldGrpcService extends HelloWorldGrpc.HelloWorldImplBase {

    @Override
    public void sayHello(HelloRequest request, StreamObserver<HelloResponse> responseObserver) {
        HelloResponse response = HelloResponse.newBuilder()
                .setMessage("Hello, " + request.getName())
                .build();
        responseObserver.onNext(response);
        responseObserver.onCompleted();
    }
}

Message Queues (z.B. RabbitMQ, Apache Kafka)

Message Queues erlauben es, Nachrichten asynchron zwischen verschiedenen Diensten zu senden. Sie bieten eine entkoppelte Kommunikation, bei der Sender und Empfänger nicht gleichzeitig aktiv sein müssen.

WebSockets

WebSockets ermöglichen eine bidirektionale Kommunikation zwischen einem Client und einem Server. Sie sind besonders für Echtzeitanwendungen wie Chats und Multiplayer-Spiele geeignet.

Datenformate für die Kommunikation

JSON (JavaScript Object Notation)

JSON ist ein weit verbreitetes, leichtgewichtiges Datenformat, das hauptsächlich für die Kommunikation über REST APIs verwendet wird. Es ist menschenlesbar und in vielen Programmiersprachen einfach zu verarbeiten.

Beispiel für JSON mit Jackson in Java:

ObjectMapper objectMapper = new ObjectMapper();
String jsonString = objectMapper.writeValueAsString(new User(1L, "John Doe"));
User user = objectMapper.readValue(jsonInput, User.class);

XML (Extensible Markup Language)

XML ist ein robustes, strukturiertes Datenformat, das besonders in älteren Systemen oder in Umgebungen mit komplexen Anforderungen an Validierung und Datenintegrität verwendet wird.

Beispiel für XML mit JAXB in Java:

JAXBContext context = JAXBContext.newInstance(User.class);
Marshaller marshaller = context.createMarshaller();
StringWriter writer = new StringWriter();
marshaller.marshal(new User(1L, "John Doe"), writer);
String xmlString = writer.toString();

Protobuf (Protocol Buffers)

Protobuf ist ein effizientes binäres Serialisierungsformat, das in gRPC verwendet wird. Es ist schneller und kompakter als JSON oder XML, insbesondere bei der Übertragung großer Datenmengen.

Avro

Avro ist ein weiteres Serialisierungsformat, das für den Datenaustausch in verteilten Systemen verwendet wird. Es wird häufig in Hadoop- und Kafka-Umgebungen eingesetzt.

Verbindungsarten und Transportprotokolle

HTTP/HTTPS

HTTP ist das Standardprotokoll für den Datenaustausch über das Web. HTTPS fügt eine Sicherheitsstufe hinzu, indem es den Datenverkehr mittels SSL/TLS verschlüsselt.

TCP/IP

TCP ist ein verbindungsorientiertes Protokoll, das eine zuverlässige und geordnete Übertragung von Daten gewährleistet. Es ist die Grundlage vieler Protokolle wie HTTP, FTP und SMTP.

Beispiel für TCP-Server und -Client in Java:

ServerSocket serverSocket = new ServerSocket(8080);
Socket clientSocket = serverSocket.accept();

UDP

UDP ist ein verbindungsloses Protokoll, das für schnelle, aber unzuverlässige Übertragungen verwendet wird. Es ist ideal für Echtzeitanwendungen wie Videostreaming.

RMI (Remote Method Invocation)

RMI ist ein Java-spezifisches Protokoll, das die Kommunikation zwischen Objekten in verschiedenen JVMs ermöglicht.

Security bei der Kommunikation

Authentifizierung (z.B. OAuth, JWT)

OAuth und JWT sind gängige Mechanismen zur Authentifizierung und Autorisierung in modernen verteilten Systemen. OAuth ermöglicht Drittanbietern den Zugriff auf Benutzerressourcen, ohne dass Zugangsdaten direkt geteilt werden.

Beispiel für JWT in Java:

String jwt = Jwts.builder()
        .setSubject("user")
        .signWith(SignatureAlgorithm.HS256, "secretKey")
        .compact();

Verschlüsselung (SSL/TLS)

SSL/TLS ist das Protokoll der Wahl, um den Datenverkehr im Internet zu verschlüsseln. Es schützt vor Abhören und Manipulation von Daten.

Rollenbasierte Zugriffssteuerung

Durch rollenbasierte Zugriffssteuerung (Role-Based Access Control, RBAC) wird sichergestellt, dass nur autorisierte Benutzer bestimmte Aktionen in einem System ausführen können.

Sicherstellung von Datenintegrität

Datenintegrität wird durch Mechanismen wie Hashing und Prüfsummen sichergestellt, um sicherzustellen, dass die Daten während der Übertragung nicht verändert wurden.

Implementierung der Kommunikation in Java

REST API mit Spring Boot

Spring Boot ist eines der populärsten Frameworks zur Implementierung von REST APIs in Java. Es vereinfacht die Konfiguration und den Aufbau von Webanwendungen.

Beispiel:


@RestController
@RequestMapping("/api")
public class UserController {

    @GetMapping("/users/{id}")
    public User getUser(@PathVariable Long id) {
        return new User(id, "John Doe");
    }

    @PostMapping("/users")
    public User createUser(@RequestBody User user) {
        user.setId(1L);
        return user;
    }
}

SOAP mit JAX-WS

JAX-WS ist eine Java-API für den Aufbau von SOAP-Webservices. Es ermöglicht die Erstellung und Konsumierung von SOAP-Nachrichten.

Beispiel:


@WebService
public class HelloWorldService {

    @WebMethod
    public String sayHello(String name) {
        return "Hello, " + name;
    }

    public static void main(String[] args) {
        Endpoint.publish("http://localhost:8080/ws/hello", new HelloWorldService());
    }
}

gRPC in Java

gRPC ist ein modernes RPC-Framework, das in verteilten Systemen verwendet wird. Es basiert auf HTTP/2 und verwendet Protocol Buffers zur Serialisierung.

Beispiel für gRPC-Server:

public class HelloWorldGrpcService extends HelloWorldGrpc.HelloWorldImplBase {

    @Override
    public void sayHello(HelloRequest request, StreamObserver<HelloResponse> responseObserver) {
        HelloResponse response = HelloResponse.newBuilder()
                .setMessage("Hello, " + request.getName())
                .build();
        responseObserver.onNext(response);
        responseObserver.onCompleted();
    }
}

Kommunikation über WebSockets

WebSockets ermöglichen eine bidirektionale Kommunikation zwischen Client und Server. Sie sind besonders nützlich für Echtzeitanwendungen wie Chats oder Spiele.

Verwendung von Message Queues (JMS, Apache Kafka)

Java Messaging Service (JMS) und Apache Kafka sind weit verbreitete Technologien zur asynchronen Kommunikation zwischen Diensten in verteilten Systemen.

Fehlerbehandlung und Ausfallsicherheit

Retry-Mechanismen und Exponential Backoff

Retry-Mechanismen sind notwendig, um temporäre Fehler in verteilten Systemen abzufangen. Exponential Backoff ist eine Strategie, bei der die Wartezeit zwischen Wiederholungsversuchen exponentiell ansteigt, um das System nicht zu überlasten.

Beispiel:

int retries = 0;
long waitTime = 1000; // 1 Sekunde
while (retries< 5) {
    try {
        // Versuche die Operation
        performOperation();
        break;
    } catch (Exception e) {
        retries++;
        Thread.sleep(waitTime);
        waitTime *=2; // Exponentielle Steigerung der Wartezeit
    }
}

Circuit Breaker Pattern

Das Circuit Breaker Pattern (Sicherung Muster) schützt ein System davor, überlastet zu werden, wenn ein abhängiger Dienst ausfällt. Es blockiert Anfragen, bis der Dienst wieder stabil ist.

  • Verhaltensmuster
  • Entdeckt wiederkehrende Verbindungsfehler
    • Trennt Verbindung (Sicherung raus)
    • Nach gewisser Zeit Verbindung testen
    • Falls OK, dann verbindet lassen, ansonsten in Trennung bleiben
  • Vergleiche elektrische Sicherung

Circuit Breaker Pattern

Fallback-Strategien

Fallback-Strategien sorgen dafür, dass das System bei Ausfällen eine Grundfunktionalität aufrechterhalten kann. Beispielsweise können alternative Datenquellen oder zwischengespeicherte Antworten verwendet werden.

Testing und Debugging der Kommunikation

Mocking von Kommunikationspartnern

In verteilten Systemen ist es oft schwierig, echte Abhängigkeiten in Tests zu verwenden. Stattdessen wird häufig Mocking eingesetzt, um die Abhängigkeiten zu simulieren.

Beispiel für das Mocken einer REST-API in Java mit Mockito:

RestTemplate restTemplate = mock(RestTemplate.class);
ResponseEntity<String> mockResponse = ResponseEntity.ok("{"id":1,"name":"John Doe"}");

when(restTemplate.getForEntity("http://example.com/api/users/1", String .class))
        .thenReturn(mockResponse);

Integrationstests für verteilte Systeme

Integrationstests stellen sicher, dass die verschiedenen Komponenten eines Systems miteinander interagieren können.

Beispiel für einen Integrationstest mit Spring Boot:

@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class RestApiIntegrationTest {

    @Autowired
    private TestRestTemplate restTemplate;

    @Test
    public void testGetUser() {
        String response = this.restTemplate.getForObject("/api/users/1", String.class);
        assertThat(response).contains("John Doe");
    }
}

Logging und Monitoring der Kommunikation

Logging und Monitoring sind entscheidend, um Probleme in der Kommunikation zu identifizieren und die Leistung von verteilten Systemen zu überwachen.

Tools für Debugging und Monitoring

Zu den Tools gehören Postman (zum Testen von APIs), Wireshark (zur Analyse von Netzwerkprotokollen) sowie Prometheus und Grafana (für die Überwachung von Systemmetriken).

Design-Muster für Testbarkeit

Design-Muster wie Dependency Injection und Test Doubles (Mocks, Stubs, Fakes) helfen, die Testbarkeit von Anwendungen zu verbessern.

Zukunft der Kommunikation zwischen heterogenen Systemen

Trends in der API-Kommunikation

Die API-Kommunikation entwickelt sich zunehmend hin zu flexibleren und effizienteren Lösungen wie GraphQL. Im Gegensatz zu REST-APIs können Clients bei GraphQL genau die Daten abfragen, die sie benötigen, was zu einer besseren Performance führt.

Mikroservices und ihre Rolle in der Systemkommunikation

Mikroservices ermöglichen es, Anwendungen in kleine, unabhängige Dienste zu unterteilen, die über das Netzwerk kommunizieren. Diese Architektur fördert die Flexibilität und Skalierbarkeit.

Serverless Architekturen

Serverless Computing ist ein aufstrebender Trend, bei dem Entwickler Code als Funktionen bereitstellen, ohne sich um die zugrunde liegende Infrastruktur kümmern zu müssen. Cloud-Anbieter wie AWS, Google und Azure bieten serverlose Plattformen wie AWS Lambda oder Google Cloud Functions an, die automatisch skalieren und nur bei Bedarf ausgeführt werden.

Beispiel für AWS Lambda in Java:

import com.amazonaws.services.lambda.runtime.Context;
import com.amazonaws.services.lambda.runtime.RequestHandler;

public class LambdaFunctionHandler implements RequestHandler<String, String> {

    @Override
    public String handleRequest(String input, Context context) {
        return "Hello, " + input + " from AWS Lambda!";
    }
}

Serverless Architekturen bieten eine hohe Flexibilität, da sie es ermöglichen, kleine, ereignisgesteuerte Anwendungen zu erstellen, die automatisch skaliert werden.

Fallbeispiel 1: Integration eines Java-Backends mit einem .NET-Frontend

Aufgabenstellung:

Ein Unternehmen möchte eine Anwendung bereitstellen, bei der das Frontend in .NET (C#) und das Backend in * Java* entwickelt wird. Das Frontend soll als Desktop- oder Webanwendung bereitgestellt werden, und das Java-Backend übernimmt die Verwaltung von Daten und Geschäftslogik, wie z.B. Benutzerdaten, Produkthandhabung und Bestellverarbeitung.

Ziel ist es, das .NET-Frontend mit dem Java-Backend zu integrieren, sodass die beiden Systeme effizient und sicher miteinander kommunizieren können.

Technologieauswahl:

  • Kommunikationsprotokoll: REST (Representational State Transfer)
  • Datenformat: JSON (JavaScript Object Notation)
  • Java-Backend-Technologie: Spring Boot für die Bereitstellung der REST-API
  • Sicherheitsstrategie: OAuth 2.0 zur Authentifizierung und JWT für Token-basierte Autorisierung
  • Verbindungsart: HTTP/HTTPS
  • Fehlerbehandlung: Retry-Mechanismen und Fallback-Strategien im .NET-Frontend
  • Testing: Integrationstests mit Mock-APIs für das .NET-Frontend und Spring Boot Tests für das Java-Backend
  • Logging und Monitoring: Prometheus und Grafana zur Überwachung

1. Protokoll: REST mit JSON

Für die Kommunikation zwischen dem .NET-Frontend und dem Java-Backend wird das REST-Protokoll verwendet, das über HTTP/HTTPS läuft. REST ist leichtgewichtig und einfach zu implementieren und bietet eine standardisierte Methode für die Interaktion zwischen den beiden Plattformen.

Das Frontend stellt Anfragen an die REST-API des Backends, und JSON wird als Datenformat verwendet, um Informationen zwischen den Systemen zu übertragen.

2. Datenformate: JSON

Die Daten zwischen dem Java-Backend und dem .NET-Frontend werden im JSON-Format ausgetauscht.

Beispiel für eine JSON-Nachricht:

{
  "id": 1,
  "name": "John Doe",
  "email": "john@example.com",
  "orders": [
    {
      "orderId": 101,
      "product": "Laptop",
      "quantity": 1
    }
  ]
}

3. Verbindungsarten und Transportprotokolle: HTTP/HTTPS

Die Kommunikation erfolgt über das HTTP-Protokoll, wobei HTTPS verwendet wird, um die Verbindung zu sichern. Alle Anfragen vom Frontend an das Backend werden über HTTP GET/POST-Anfragen abgewickelt.

4. Security bei der Kommunikation: OAuth 2.0 und JWT

Die Sicherheitsarchitektur verwendet OAuth 2.0 zur Authentifizierung und JWT (JSON Web Tokens) zur Autorisierung.

Beispiel eines JWT-Tokens:

{
  "token": "eyJhbGciOiJIUzI1NiIsIn..."
}

5. Implementierung der Kommunikation in Java: Spring Boot

Das Java-Backend wird in Spring Boot implementiert, das die REST-API bereitstellt.

Beispiel für eine REST-API in Java:

@RestController
@RequestMapping("/api/users")
public class UserController {

    @GetMapping("/{id}")
    public User getUser(@PathVariable Long id) {
        return userService.findUserById(id);
    }

    @PostMapping
    public User createUser(@RequestBody User user) {
        return userService.saveUser(user);
    }
}

6. Fehlerbehandlung und Ausfallsicherheit

Retry-Mechanismen und Fallback-Strategien werden im .NET-Frontend implementiert.

Beispiel für einen Retry-Mechanismus:

int retries = 0;
int waitTime = 1000; // 1 Sekunde
while (retries < 5)
{
    try 
    {
        HttpResponseMessage response = client.GetAsync("/api/users/1").Result;
        if (response.IsSuccessStatusCode)
        {
            break;
        }
    } 
    catch 
    {
        retries++;
        Thread.Sleep(waitTime);
        waitTime *= 2; // Exponentielles Backoff
    }
}

7. Testing und Debugging der Kommunikation

Mocking von APIs wird eingesetzt, um das Frontend unabhängig vom Backend zu testen.

Integrationstests in Spring Boot:

@SpringBootTest
public class UserControllerTest {

    @Autowired
    private MockMvc mockMvc;

    @Test
    public void testGetUser() throws Exception {
        mockMvc.perform(get("/api/users/1"))
                .andExpect(status().isOk())
                .andExpect(content().json("{"id":1,"name":"John Doe"}"));
    }
}

Logging und Monitoring werden mit Prometheus und Grafana durchgeführt, um die Systemmetriken in Echtzeit zu überwachen.

Fallbeispiel 2: Kommunikation zwischen einem IoT-Gerät und einem Cloud-Service

Aufgabenstellung:

Ein Unternehmen möchte eine Lösung entwickeln, bei der IoT-Geräte (z.B. Sensoren oder smarte Haushaltsgeräte) Daten erfassen und diese an einen Cloud-Service senden. Der Cloud-Service verarbeitet die Daten und stellt sie für weitere Auswertungen oder Aktionen zur Verfügung.

Technologieauswahl:

  • Kommunikationsprotokoll: MQTT (Message Queuing Telemetry Transport) zur Kommunikation zwischen dem IoT-Gerät und dem Cloud-Service
  • Datenformat: JSON für die Datenerfassung und -übertragung
  • Cloud-Service: AWS IoT Core oder Azure IoT Hub für das Management und die Verarbeitung der eingehenden IoT-Daten
  • Sicherheitsstrategie: TLS/SSL für verschlüsselte Verbindungen und X.509-Zertifikate für die Authentifizierung der IoT-Geräte
  • Verbindungsart: TCP/IP über MQTT
  • Fehlerbehandlung: Retry-Mechanismen und QoS (Quality of Service)-Stufen für die Übertragungssicherheitrwachung und Fehlerbehebung

Technologieauswahl:

  • Testing: Simulierte IoT-Geräte für die Tests der Cloud-Anbindung, Monitoring der Datenströme in der Cloud
  • Logging und Monitoring: AWS CloudWatch oder Azure Monitor zur Überwachung und Fehlerbehebung

1. Protokoll: MQTT

MQTT ist das bevorzugte Protokoll für IoT-Anwendungen, da es leichtgewichtig ist und speziell für die Übertragung von Nachrichten über instabile oder begrenzte Netzwerke entwickelt wurde.

Beispiel für eine MQTT-Nachricht:

{
  "deviceId": "sensor123",
  "temperature": 22.5,
  "humidity": 60
}

2. Datenformate: JSON

Für die Kommunikation zwischen dem IoT-Gerät und dem Cloud-Service wird JSON verwendet.

Beispiel einer JSON-Datenstruktur:

{
  "deviceId": "sensor123",
  "timestamp": "2024-10-08T10:00:00Z",
  "data": {
    "temperature": 22.5,
    "humidity": 60
  }
}

3. Verbindungsarten und Transportprotokolle: TCP/IP über MQTT

Die Verbindung zwischen dem IoT-Gerät und dem Cloud-Service erfolgt über das MQTT-Protokoll, das auf TCP/IP basiert.

4. Security bei der Kommunikation: TLS/SSL und X.509-Zertifikate

Die Sicherheit der Kommunikation wird durch TLS/SSL und X.509-Zertifikate gewährleistet.

Beispiel einer TLS-Verbindung:

mosquitto_pub --cafile rootCA.pem \
              --cert deviceCert.crt \
              --key deviceKey.key \
              -h iot.example.com \
              -p 8883 \
              -t "iot/sensors/temperature" -m "..."

5. Implementierung der Kommunikation in IoT-Geräten

Beispiel in Python für ein IoT-Gerät, das über MQTT mit AWS IoT Core kommuniziert:

import paho.mqtt.client as mqtt
import json

# MQTT Callback-Funktion
def on_connect(client, userdata, flags, rc):
    print(f"Connected with result code {rc}")
    client.publish("iot/sensors/temperature",
                   json.dumps({"deviceId": "sensor123", 
                   "temperature": 22.5, 
                   "humidity": 60}))

# MQTT-Client konfigurieren
client = mqtt.Client()
client.tls_set(ca_certs="rootCA.pem",
               certfile="deviceCert.crt",
               keyfile="deviceKey.key")
client.connect("iot.example.com", 8883, 60)

# Verbindung herstellen und Nachrichten senden
client.on_connect = on_connect
client.loop_forever()

6. Fehlerbehandlung und Ausfallsicherheit

Beispiel für die Nutzung von QoS in einem Python-MQTT-Client:

client.publish("iot/sensors/temperature", payload=json_data, qos=1)

7. Testing und Debugging der Kommunikation

Monitoring der Datenströme in AWS:

aws iot get-thing-shadow --thing-name "sensor123"

Zusammenfassung:

Für die Kommunikation zwischen einem IoT-Gerät und einem Cloud-Service wird:

  • MQTT als Kommunikationsprotokoll verwendet.
  • JSON dient als Datenformat zur Übertragung der Sensordaten.
  • TLS/SSL und X.509-Zertifikate sichern die Verbindung und Authentifizierung der IoT-Geräte.
  • Retry-Mechanismen und QoS-Stufen garantieren die Zuverlässigkeit.
  • Simulierte IoT-Geräte und Cloud-Monitoring-Tools sorgen für umfassende Tests und Debugging.

Quellen

TBD

06 Einführung in heterogene Systeme

By Harald Haberstroh

06 Einführung in heterogene Systeme

  • 16