Modul 321 Alle Aufgaben

Aufgabe 6: Deklarative Service-Kommunikation mit Spring Cloud OpenFeign

Zielsetzung

In einer Microservice-Architektur kommunizieren Services laufend über das Netzwerk miteinander. Häufig greift ein aufrufender Service auf die REST-API eines entfernten Fachservices zu.

Herkömmliche HTTP-Clients wie RestTemplate oder WebClient erfordern viel wiederkehrenden Code: URLs müssen manuell zusammengesetzt, Header gesetzt, Fehlercodes geprüft und JSON-Datenstrukturen deserialisiert werden. Ausserdem müssen IP-Adressen und Ports verwaltet werden.

Spring Cloud OpenFeign vereinfacht diese Kommunikation durch einen deklarativen Ansatz: Sie definieren ein Java-Interface mit bekannten Spring-MVC-Annotationen (@GetMapping, @PostMapping). Spring erzeugt zur Laufzeit die Implementierung, bindet die Service-Erkennung über Eureka ein und übernimmt das clientseitige Routing.

In dieser Aufgabe lernen Sie:

  1. Wie Sie @EnableFeignClients in Spring Boot aktivieren.
  2. Wie Sie mit @FeignClient eine Schnittstelle zu einem entfernten Microservice deklarieren.
  3. Wie Feign Servicenamen dynamisch über Eureka auflöst, ohne feste IP-Adressen oder Portnummern.
  4. Wie Datenobjekte (DTOs) über Servicegrenzen hinweg ausgetauscht werden.
  5. Welches Fehlerverhalten auftritt, wenn ein abhängiger Service nicht erreichbar ist.

Material & Vorlagen

Das Archiv enthält vier Module:


Die Architektur im Überblick

flowchart TD
    E["Eureka Server (Port 8761)<br>Service Registry"]
    S1["Service-01 (Port 8080)<br>@EnableFeignClients"]
    S2["Service-02 (Port 8081)<br>@RestController"]

    S2 -.->|"Registriert sich als service-02"| E
    S1 -.->|"1. Lookup: Wo laeuft service-02?"| E
    S1 -->|"2. HTTP GET /books (deklarativ via Feign)"| S2
    S2 -->|"Liefert JSON: List&lt;Book&gt;"| S1

Vorgehen

Teil 1: Projektstruktur und Service-02 prüfen

  1. Entpacken Sie feign-starter.zip und öffnen Sie den Ordner in IntelliJ IDEA als Maven-Projekt.
  2. Starten Sie zuerst eureka-server-app (EurekaServerAppApplication.java, Port 8761).
  3. Öffnen Sie http://localhost:8761 im Browser. Das Eureka Dashboard sollte sichtbar sein.
  4. Untersuchen Sie das Modul service-02:
    Öffnen Sie RESTBookController.java. Der Controller verwaltet eine Liste von Büchern im Arbeitsspeicher:
    @RestController
    public class RESTBookController {
        private List<Book> books = new ArrayList<>();
    
        public RESTBookController() {
            books.add(new Book(1, "The Metamorphosis", "Franz Kafka"));
            books.add(new Book(2, "The Trial", "Franz Kafka"));
            books.add(new Book(3, "1984", "George Orwell"));
            books.add(new Book(4, "Animal Farm", "George Orwell"));
        }
    
        @GetMapping("books")
        public List<Book> getBooks() {
            return books;
        }
    }
  5. Starten Sie service-02 (Service02Application.java, Port 8081).
  6. Kontrollieren Sie:

Teil 2: Service-01 als Feign-Client vorbereiten

  1. Öffnen Sie die Datei pom.xml in service-01.
  2. Überprüfen Sie, ob die OpenFeign-Dependency eingebunden ist:
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-openfeign</artifactId>
    </dependency>
  3. Öffnen Sie die Hauptklasse Service01Application.java und aktivieren Sie Feign mit der Annotation @EnableFeignClients:
    @SpringBootApplication
    @EnableDiscoveryClient
    @EnableFeignClients
    public class Service01Application {
        public static void main(String[] args) {
            SpringApplication.run(Service01Application.class, args);
        }
    }

Teil 3: Feign-Client-Interface deklarieren

Mit Feign deklarieren Sie lediglich das gewünschte Interface:

  1. Erstellen Sie im Modul service-01 das DTO Book.java (falls noch nicht vorhanden):
    public class Book {
        private int id;
        private String title;
        private String author;
    
        // Konstruktoren, Getter und Setter
    }
  2. Erstellen Sie ein neues Interface BooksClient.java:
    package ch.bbw.service01;
    
    import org.springframework.cloud.openfeign.FeignClient;
    import org.springframework.web.bind.annotation.GetMapping;
    import java.util.List;
    
    @FeignClient("service-02")
    public interface BooksClient {
    
        @GetMapping("/books")
        List<Book> getBooks();
    }
    Hinweis: Der Bezeichner "service-02" in @FeignClient entspricht dem Anwendungsnamen in Eureka. Feign fragt Eureka automatisch nach der aktuellen Adresse und dem Port.

Teil 4: Feign-Client im Controller nutzen

Nun stellt service-01 einen Endpunkt /api/books bereit, der intern über Feign die Daten aus service-02 abruft und formatiert zurückgibt:

  1. Öffnen Sie MainController.java in service-01 und injizieren Sie den BooksClient:
    @RestController
    public class MainController {
    
        @Autowired
        private BooksClient booksClient;
    
        @GetMapping("/api/books")
        public List<String> getBooksFormatted() {
            List<Book> books = booksClient.getBooks();
    
            return books.stream()
                    .map(book -> String.format("'%s' von %s (ID: %d)", 
                            book.getTitle(), book.getAuthor(), book.getId()))
                    .toList();
        }
    }
  2. Starten Sie service-01 (Port 8080).
  3. Rufen Sie im Browser http://localhost:8080/api/books auf.

Teil 5: Endpunkt erweitern (POST via Feign)

Bisher erfolgte der Zugriff lesend (GET). Nun soll service-01 auch neue Bücher in service-02 anlegen können:

  1. Erweitern Sie in service-02 den RESTBookController um einen POST-Endpunkt:
    @PostMapping("books")
    public ResponseEntity<Book> addBook(@RequestBody Book book) {
        books.add(book);
        return ResponseEntity.status(HttpStatus.CREATED).body(book);
    }
  2. Fügen Sie die entsprechende Methode im Feign-Interface BooksClient.java in service-01 hinzu:
    @PostMapping("/books")
    Book createBook(@RequestBody Book book);
  3. Fügen Sie in service-01 (MainController.java) eine Methode hinzu, um das Erstellen zu testen:
    @PostMapping("/api/books")
    public Book addNewBook(@RequestBody Book book) {
        return booksClient.createBook(book);
    }
  4. Senden Sie mit Postman oder curl einen POST-Request an http://localhost:8080/api/books:
    curl -X POST http://localhost:8080/api/books \
      -H "Content-Type: application/json" \
      -d '{"id": 5, "title": "Harry Potter and the Philosopher'\''s Stone", "author": "J.K. Rowling"}'
  5. Rufen Sie http://localhost:8080/api/books erneut auf.

Teil 6: Verhalten bei Service-Ausfall

  1. Stoppen Sie service-02 in IntelliJ.
  2. Rufen Sie erneut http://localhost:8080/api/books auf.
  3. Beobachtung: Fällt ein abhängiger Service aus, schlägt der Aufruf im aufrufenden Service fehl (kaskadierender Fehler).
  4. Ausblick: Im späteren Verlauf des Moduls lernen Sie Schutzmechanismen kennen (Circuit Breaker mit Resilience4j), um solche Fehler gezielt abzufangen.

Abgabe

Dokumentieren Sie Ihre Ergebnisse:

  1. Quellcode Ihres Interfaces BooksClient.java.
  2. Screenshot des Postman-Requests oder der Browser-Ausgabe auf /api/books mit dem neu hinzugefügten Buch.
  3. Architektur-Frage: