Feing client vs web client (#12968)

* implement code example for feignClient vs WebClient

* refactor code example for feignClient vs WebClient with /products endpoint

* fix PR comment
This commit is contained in:
Vlad Fernoaga 2022-11-29 04:23:11 +02:00 committed by GitHub
parent f6ae9ceefa
commit 28c0b9569e
8 changed files with 198 additions and 1 deletions

View File

@ -20,6 +20,14 @@
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>org.projectreactor</groupId>
<artifactId>reactor-spring</artifactId>
@ -35,10 +43,27 @@
<artifactId>reactor-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<properties>
<reactor-spring.version>1.0.1.RELEASE</reactor-spring.version>
<spring-cloud.version>2021.0.4</spring-cloud.version>
</properties>
</project>

View File

@ -0,0 +1,13 @@
package com.baeldung.webclient;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Product {
private String title;
private String description;
}

View File

@ -0,0 +1,15 @@
package com.baeldung.webclient;
import java.net.URI;
import java.util.List;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
@FeignClient(value = "productsBlocking", url = "http://localhost:8080")
public interface ProductsFeignClient {
@RequestMapping(method = RequestMethod.GET, value = "/slow-service-products", produces = "application/json")
List<Product> getProductsBlocking(URI baseUrl);
}

View File

@ -0,0 +1,21 @@
package com.baeldung.webclient;
import java.util.Arrays;
import java.util.List;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class ProductsSlowServiceController {
@GetMapping("/slow-service-products")
private List<Product> getAllProducts() throws InterruptedException {
Thread.sleep(2000L); // delay
return Arrays.asList(
new Product("Fancy Smartphone", "A stylish phone you need"),
new Product("Cool Watch", "The only device you need"),
new Product("Smart TV", "Cristal clean images")
);
}
}

View File

@ -0,0 +1,15 @@
package com.baeldung.webclient;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.openfeign.EnableFeignClients;
@SpringBootApplication
@EnableFeignClients
public class WebClientApplication {
public static void main(String[] args) {
SpringApplication.run(WebClientApplication.class, args);
}
}

View File

@ -0,0 +1,59 @@
package com.baeldung.webclient;
import java.net.URI;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.reactive.function.client.WebClient;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import reactor.core.publisher.Flux;
@Slf4j
@RestController
public class WebController {
private static final int DEFAULT_PORT = 8080;
public static final String SLOW_SERVICE_PRODUCTS_ENDPOINT_NAME = "/slow-service-products";
@Setter
private int serverPort = DEFAULT_PORT;
@Autowired
private ProductsFeignClient productsFeignClient;
@GetMapping("/products-blocking")
public List<Product> getProductsBlocking() {
log.info("Starting BLOCKING Controller!");
final URI uri = URI.create(getSlowServiceBaseUri());
List<Product> result = productsFeignClient.getProductsBlocking(uri);
result.forEach(product -> log.info(product.toString()));
log.info("Exiting BLOCKING Controller!");
return result;
}
@GetMapping(value = "/products-non-blocking", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<Product> getProductsNonBlocking() {
log.info("Starting NON-BLOCKING Controller!");
Flux<Product> productFlux = WebClient.create()
.get()
.uri(getSlowServiceBaseUri() + SLOW_SERVICE_PRODUCTS_ENDPOINT_NAME)
.retrieve()
.bodyToFlux(Product.class);
productFlux.subscribe(product -> log.info(product.toString()));
log.info("Exiting NON-BLOCKING Controller!");
return productFlux;
}
private String getSlowServiceBaseUri() {
return "http://localhost:" + serverPort;
}
}

View File

@ -0,0 +1,49 @@
package com.baeldung.webclient;
import static org.springframework.test.annotation.DirtiesContext.ClassMode.BEFORE_CLASS;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.boot.test.context.SpringBootTest.WebEnvironment;
import org.springframework.boot.test.web.server.LocalServerPort;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.web.reactive.server.WebTestClient;
@DirtiesContext(classMode = BEFORE_CLASS)
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT, classes = WebClientApplication.class)
class WebControllerIntegrationTest {
@LocalServerPort
private int randomServerPort;
@Autowired
private WebTestClient testClient;
@Autowired
private WebController webController;
@BeforeEach
void setup() {
webController.setServerPort(randomServerPort);
}
@Test
void whenEndpointWithBlockingClientIsCalled_thenThreeProductsAreReceived() {
testClient.get()
.uri("/products-blocking")
.exchange()
.expectStatus().isOk()
.expectBodyList(Product.class).hasSize(3);
}
@Test
void whenEndpointWithNonBlockingClientIsCalled_thenThreeProductsAreReceived() {
testClient.get()
.uri("/products-non-blocking")
.exchange()
.expectStatus().isOk()
.expectBodyList(Product.class).hasSize(3);
}
}