Merge pull request #15334 from etrandafir93/features/BAEL-7143-virtual_threads_vs_webflux_2

Features/bael 7143 virtual threads vs webflux 2
This commit is contained in:
davidmartinezbarua 2023-12-15 14:11:09 -03:00 committed by GitHub
commit 9910369106
17 changed files with 340 additions and 0 deletions

View File

@ -30,6 +30,9 @@
<module>spring-reactive-exceptions</module>
<module>spring-reactor</module>
<module>spring-webflux-amqp</module>
<!-- the following submodules are commented out as a workaround in order to use java 19+ and SB 3.2.x -->
<!-- <module>spring-reactive-performance</module>-->
</modules>
<build>

View File

@ -0,0 +1,25 @@
/target/
!.mvn/wrapper/maven-wrapper.jar
### STS ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache
### IntelliJ IDEA ###
.idea
*.iws
*.iml
*.ipr
### NetBeans ###
/nbproject/private/
/build/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/

View File

@ -0,0 +1,3 @@
## Spring Reactive Performance
This module contains articles about reactive Spring Boot.

View File

@ -0,0 +1,77 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.baeldung.spring</groupId>
<artifactId>spring-reactive-performance</artifactId>
<version>1.0.0-SNAPSHOT</version>
<name>spring-reactive-performance</name>
<packaging>jar</packaging>
<description>Spring Reactive Performance</description>
<parent>
<groupId>com.baeldung.spring.reactive</groupId>
<artifactId>spring-reactive-modules</artifactId>
<version>1.0.0-SNAPSHOT</version>
</parent>
<dependencyManagement>
<dependencies>
<dependency>
<!-- Import dependency management from Spring Boot -->
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>${spring-boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>21</source>
<target>21</target>
<!-- Needed due to a bug with JDK 21, described here: https://issues.apache.org/jira/browse/MCOMPILER-546?page=com.atlassian.jira.plugin.system.issuetabpanels%3Acomment-tabpanel&focusedCommentId=17767513 -->
<debug>false</debug>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<argLine>--enable-preview</argLine>
</configuration>
</plugin>
</plugins>
</build>
<properties>
<java.version>21</java.version>
<spring-boot.version>3.2.0</spring-boot.version>
</properties>
</project>

View File

@ -0,0 +1,13 @@
package com.baeldung.spring.reactive.performance;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}

View File

@ -0,0 +1,14 @@
package com.baeldung.spring.reactive.performance;
import java.util.concurrent.CompletableFuture;
public class KafkaTemplate<K, V> {
// For simplicity in this example and article, an actual Kafka client isn't utilized.
// The focus remains on demonstrating the basic principles without the complexities of a full Kafka client setup.
public CompletableFuture<Void> send(String topic, K key, V value) {
System.out.println("Sending message to topic: " + topic + " with value: " + value);
return CompletableFuture.completedFuture(null);
}
}

View File

@ -0,0 +1,6 @@
package com.baeldung.spring.reactive.performance;
import java.math.BigDecimal;
public record ProductAddedToCartEvent(String productId, BigDecimal price, String currency, String cartId) {
}

View File

@ -0,0 +1,9 @@
package com.baeldung.spring.reactive.performance.model;
import java.math.BigDecimal;
public record Price(BigDecimal value, String currency) {
public Price applyDiscount(BigDecimal discount) {
return new Price(value.subtract(discount), currency);
}
}

View File

@ -0,0 +1,43 @@
package com.baeldung.spring.reactive.performance.model;
import java.math.BigDecimal;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;
@Document(collation = "products")
public record Product (
@Id
String id,
String name,
BigDecimal basePriceValue,
String currency,
Category category
) {
public Price basePrice() {
return new Price(basePriceValue, currency);
}
public enum Category {
ELECTRONICS(false),
CLOTHING(true),
ACCESSORIES(false),
GARDENING(false),
SPORTS(true);
private final boolean eligibleForPromotion;
Category(boolean eligibleForPromotion) {
this.eligibleForPromotion = eligibleForPromotion;
}
public boolean isEligibleForDiscount() {
return eligibleForPromotion;
}
public boolean notEligibleForPromotion() {
return !eligibleForPromotion;
}
}
}

View File

@ -0,0 +1,11 @@
package com.baeldung.spring.reactive.performance.virtualthreads;
import java.math.BigDecimal;
class DiscountService {
public BigDecimal discountForProduct(String productId) {
return BigDecimal.valueOf(10);
}
}

View File

@ -0,0 +1,8 @@
package com.baeldung.spring.reactive.performance.virtualthreads;
import org.springframework.data.mongodb.repository.MongoRepository;
import com.baeldung.spring.reactive.performance.model.Product;
interface ProductRepository extends MongoRepository<Product, String> {
}

View File

@ -0,0 +1,45 @@
package com.baeldung.spring.reactive.performance.virtualthreads;
import java.math.BigDecimal;
import com.baeldung.spring.reactive.performance.KafkaTemplate;
import com.baeldung.spring.reactive.performance.ProductAddedToCartEvent;
import com.baeldung.spring.reactive.performance.model.Price;
import com.baeldung.spring.reactive.performance.model.Product;
class ProductService {
private final String PRODUCT_ADDED_TO_CART_TOPIC = "product-added-to-cart";
private final ProductRepository repository;
private final DiscountService discountService;
private final KafkaTemplate<String, ProductAddedToCartEvent> kafkaTemplate;
public ProductService(ProductRepository repository, DiscountService discountService) {
this.repository = repository;
this.discountService = discountService;
this.kafkaTemplate = new KafkaTemplate<>();
}
public void addProductToCart(String productId, String cartId) {
Thread.startVirtualThread(() -> computePriceAndPublishMessage(productId, cartId));
}
private void computePriceAndPublishMessage(String productId, String cartId) {
Product product = repository.findById(productId)
.orElseThrow(() -> new IllegalArgumentException("not found!"));
Price price = computePrice(productId, product);
var event = new ProductAddedToCartEvent(productId, price.value(), price.currency(), cartId);
kafkaTemplate.send(PRODUCT_ADDED_TO_CART_TOPIC, cartId, event);
}
private Price computePrice(String productId, Product product) {
if (product.category().isEligibleForDiscount()) {
BigDecimal discount = discountService.discountForProduct(productId);
return product.basePrice().applyDiscount(discount);
}
return product.basePrice();
}
}

View File

@ -0,0 +1,11 @@
package com.baeldung.spring.reactive.performance.webflux;
import java.math.BigDecimal;
import reactor.core.publisher.Mono;
class DiscountService {
public Mono<BigDecimal> discountForProduct(String productId) {
return Mono.just(BigDecimal.valueOf(10));
}
}

View File

@ -0,0 +1,8 @@
package com.baeldung.spring.reactive.performance.webflux;
import org.springframework.data.mongodb.repository.ReactiveMongoRepository;
import com.baeldung.spring.reactive.performance.model.Product;
interface ProductRepository extends ReactiveMongoRepository<Product, String> {
}

View File

@ -0,0 +1,39 @@
package com.baeldung.spring.reactive.performance.webflux;
import com.baeldung.spring.reactive.performance.KafkaTemplate;
import com.baeldung.spring.reactive.performance.ProductAddedToCartEvent;
import com.baeldung.spring.reactive.performance.model.Price;
import com.baeldung.spring.reactive.performance.model.Product;
import reactor.core.publisher.Mono;
class ProductService {
private final String PRODUCT_ADDED_TO_CART_TOPIC = "product-added-to-cart";
private final ProductRepository repository;
private final DiscountService discountService;
private final KafkaTemplate<String, ProductAddedToCartEvent> kafkaTemplate;
public ProductService(ProductRepository repository, DiscountService discountService) {
this.repository = repository;
this.discountService = discountService;
this.kafkaTemplate = new KafkaTemplate<>();
}
public void addProductToCart(String productId, String cartId) {
repository.findById(productId)
.switchIfEmpty(Mono.error(() -> new IllegalArgumentException("not found!")))
.flatMap(this::computePrice)
.map(price -> new ProductAddedToCartEvent(productId, price.value(), price.currency(), cartId))
.subscribe(event -> kafkaTemplate.send(PRODUCT_ADDED_TO_CART_TOPIC, cartId, event));
}
private Mono<Price> computePrice(Product product) {
if (product.category().isEligibleForDiscount()) {
return discountService.discountForProduct(product.id())
.map(product.basePrice()::applyDiscount);
}
return Mono.just(product.basePrice());
}
}

View File

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
</pattern>
</encoder>
</appender>
<root level="INFO">
<appender-ref ref="STDOUT" />
</root>
</configuration>

View File

@ -0,0 +1,12 @@
package com.baeldung.spring.reactive.performance;
import org.junit.Test;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
public class ApplicationUnitTest {
@Test
public void whenSpringContextIsBootstrapped_thenNoExceptions() {
}
}