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:
@EnableFeignClients in Spring Boot
aktivieren.@FeignClient eine Schnittstelle zu einem
entfernten Microservice deklarieren.feign-starter.zipDas Archiv enthält vier Module:
eureka-server-app (Port 8761):
Service-Registryservice-02 (Port 8081): REST-Backend mit
Bücherkatalogservice-01 (Port 8080): Aufrufender
Service (Frontend und Aggregator)app-gateway (Port 8079): API-Gatewayflowchart 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<Book>"| S1
feign-starter.zip
und öffnen Sie den Ordner in IntelliJ IDEA als Maven-Projekt.eureka-server-app
(EurekaServerAppApplication.java, Port
8761).http://localhost:8761 im Browser. Das Eureka
Dashboard sollte sichtbar sein.service-02: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;
}
}service-02
(Service02Application.java, Port 8081).pom.xml in
service-01.<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>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);
}
}Mit Feign deklarieren Sie lediglich das gewünschte Interface:
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
}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();
}"service-02" in
@FeignClient entspricht dem Anwendungsnamen in Eureka.
Feign fragt Eureka automatisch nach der aktuellen Adresse und dem
Port.Nun stellt service-01 einen Endpunkt
/api/books bereit, der intern über Feign die Daten aus
service-02 abruft und formatiert zurückgibt:
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();
}
}service-01 (Port 8080).http://localhost:8080/api/books
auf.
Bisher erfolgte der Zugriff lesend (GET). Nun soll
service-01 auch neue Bücher in service-02
anlegen können:
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);
}BooksClient.java in service-01 hinzu:
@PostMapping("/books")
Book createBook(@RequestBody Book book);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);
}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"}'http://localhost:8080/api/books erneut auf.
service-02 in IntelliJ.http://localhost:8080/api/books auf.
Dokumentieren Sie Ihre Ergebnisse:
BooksClient.java./api/books mit dem neu hinzugefügten Buch.service-01 an keiner Stelle die Adresse
localhost oder der Port 8081 für
service-02 angegeben werden? Wie hat Feign die Verbindung
aufgebaut?