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/

Zobacz kod na GitHubie i zapisz się na bezpłatny newsletter!

.

Leave a comment

Your email address will not be published.


*