Spring Cloud Feign
Spring Cloud Feign
Feign to deklaratywny klient HTTP który stanowi warstwę abstrakcji pomiędzy zapytaniami REST a aplikacją. Developer nie musi wykonywać bezpośrednich zapytań do zasobu zamiast tego tworzy interfejs którego implementację możemy wstrzyknąć jak zwykłego Spring Beana:
@FeignClient(url = "http://example.com") public interface UserData { @RequestMapping(method = RequestMethod.GET, value = "/users") User getUser(@Param int userId);
Wstrzyknięcie do kontrolera:
@RestController class UserController{ @Autowired private UserData userData; @GetMapping("/users/{userId}") public User gerUser(@PathVariable int userId) { return userData.getUser(userId); } }
Aby zademonstrować w praktyce użycie mechanizmu FeignClient utworzymy dwa mikroserwisy, jeden odpowiada za dostarczanie informacji o produktach, drugi z kolei odpowiada za konsumowanie tej usługi. Dodatkowo mikroserwis dostarczający informacji o produktach zarejestrowany zostanie w usłudze Service Discovery – Eureka (http://spring.io/guides/gs/service-registration-and-discovery) Do dzieła!
Tworzymy projekt – spring-cloud-feign-eureka-server – niezbędne zależności:
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka-server</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka</artifactId> </dependency> <dependency> <groupId>com.hazelcast</groupId> <artifactId>hazelcast-client</artifactId> <version>3.10.3</version> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>Camden.SR6</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement>
Klasa startowa:
@SpringBootApplication @EnableEurekaServer public class SpringCloudFeignEurekaServerApplication { public static void main(String[] args) { SpringApplication.run(SpringCloudFeignEurekaServerApplication.class, args); } }
Plik konfiguracyjny appliacation.yml – domyślnym portem dla usługi service discovery eureka jest 8761:
server: port: 8761 eureka: instance: hostname: localhost serviceUrl: defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
Tworzymy projekt spring-cloud-feign-product-service – wystawia API niezbędne do uzyskania informacji o produktach – niezbędne zależności:
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>Camden.SR6</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement>
RestController:
@RestController public class ProductController { private static List<Product> productList = new ArrayList(); static { productList.add(new Product(1, "product-1", 12.0)); productList.add(new Product(2, "product-2", 34.0)); productList.add(new Product(3, "product-3", 9.0)); } @GetMapping("/products") public ResponseEntity <?> getProsucts() { return ResponseEntity.ok(productList); } @GetMapping("/product/{id}") public ResponseEntity <?> getProsucts(@PathVariable int id) { Product product = findProduct(id); if (product == null) { return ResponseEntity.badRequest() .body("Invalid product Id"); } return ResponseEntity.ok(product); } private Product findProduct(int id) { return productList.stream() .filter(user -> user.getId() .equals(id)) .findFirst() .orElse(null); } }
Klasa modelu:
public class Product { private Integer id; private String name; private double price; public Product() { } public Product(Integer id, String name, double price) { this.id = id; this.name = name; this.price = price; } // getters & setters }
Plik konfiguracyjny application.properties:
server.port = 8111 spring.application.name = product-service
Tworzymy projekt spring-cloud-feign-client – konsumuje informacje o produktach – niezbędne zależności:
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> <version>2.0.2.RELEASE</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> <version>2.0.2.RELEASE</version> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-dependencies</artifactId> <version>2.0.0.M3</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement>
Klasa startowa:
@SpringBootApplication @EnableDiscoveryClient @EnableFeignClients public class SpringCloudFeignClientApplication { public static void main(String[] args) { SpringApplication.run(SpringCloudFeignClientApplication.class, args); } }
RestControler:
@RestController public class AppController { @Autowired ProductServiceClient productServiceClient; @GetMapping("/fetchProducts") public ResponseEntity<?> fetchProducts() { return ResponseEntity.ok(productServiceClient.getAllProducts()); } @GetMapping("/fetchProduct/{id}") public ResponseEntity<?> fetchProduct(@PathVariable int id) { Product product = productServiceClient.getProduct(id); return ResponseEntity.ok(product); } }
Klasa modelu:
public class Product { private Integer id; private String name; private double price; public Product() { } public Product(Integer id, String name, double price) { this.id = id; this.name = name; this.price = price; } // getters & setters }
Serwis – FeignClient:
@FeignClient(name = "product-service"/*, url = "http://localhost:8081"*/) public interface ProductServiceClient { @RequestMapping(value = "/products", method = RequestMethod.GET) List<Product> getAllProducts(); @RequestMapping(value = "/product/{id}", method = RequestMethod.GET) Product getProduct(@PathVariable("id") int productId); }
Plik konfiguracyjny application.properties:
server.port = 8942 spring.application.name = feign-client-example
Testy:
zwraca informacje o konkretnym produkcie:
http://localhost:8942/fetchProduct/2
zwraca informacje o wszystkich prouktach:
http://localhost:8942/fetchProducts/
Leave a comment