- Start mockserver
- Go to mockserver folder and type docker-compose up -d
- Go to http://localhost:1080/mockserver/dashboard to see loaded expectations
- No mockserver restart needed when updating cfdata.json
- Start spring boot app CfApplication: ./mvnw spring-boot:run
- Go to http://localhost:8080/productsv1
- Look at log to check duration of the call
Scenario:
Return list of products including price, stock level, reviews with 5 different services
- http://localhost:1080/products list products including productId
- http://localhost:1080/prices?productId={} price for product (May not produce error or no data, if error, skip product)
- http://localhost:1080/warehouses?productId={} returns locationId for stock query (if error return stock unknown)
- http://localhost:1080/locations?locationId={}&productId={} returns stock for product (if error return stock unknown)
- http://localhost:1080/reviews?productId={} list of reviews, on error empty list
ProductDataService Blocking calls for all
Steps:
- Open ProductService2 and analyze each call
- Convert calls into CompletableFuture calls
- Update code to run futures in parallel where applicable
- Go to http://localhost:8080/productsv2
- Compare duration in logs with duration of productsv1 call
- Make it even faster!!
Starting tips
- Changing call to ComputableFuture
private CompletableFuture<RemoteProductPrices> getProductPrice(String productId) {
return CompletableFuture.supplyAsync(() -> {
return restTemplate.getForObject("/prices?productId=" + productId, RemoteProductPrices.class);
});
}
-
Handling exceptions .exceptionally
-
Use result of ComputalbleFuture or create something new .thenApply "map" or .thenCompose "flatMap"
-
User custom thread pool for execution, tweak values for more performance
ThreadPoolTaskExecutor myExcutor = new ThreadPoolTaskExecutor();
myExcutor.setCorePoolSize(2);
myExcutor.setMaxPoolSize(2);
myExcutor.setQueueCapacity(500);
myExcutor.setThreadNamePrefix("MyCustomPool-");
myExcutor.initialize();
more info:
https://howtodoinjava.com/java/multi-threading/java-thread-pool-executor-example/
https://dzone.com/articles/be-aware-of-forkjoinpoolcommonpool
- Monitor threads by looking at spring boot metrics actuator. Add management.endpoints.web.exposure.include: '*' to application.yml and create metric for your pool as in example below
@Bean
public ExecutorService myExecutor(final MeterRegistry registry) {
return ExecutorServiceMetrics
.monitor(registry, Executors.newFixedThreadPool(10), "myExecutorPool");
}
- Configure HttpClient for your rest templates
@Bean
public HttpClient myHttpClient(MyProps properties) {
HttpClientBuilder httpClientBuilder = HttpClientBuilder.create()
.setDefaultRequestConfig(
RequestConfig.custom()
.setConnectTimeout(Math.toIntExact(properties.getConnectionTimeout().toMillis()))
.setSocketTimeout(Math.toIntExact(properties.getReadTimeout().toMillis()))
.build())
.setMaxConnTotal(100)
.setMaxConnPerRoute(50);
return httpClientBuilder.build();
}
@Bean
RestTemplate myTemplate(RestTemplateBuilder builder, MyProps properties) {
return builder
.requestFactory(() -> new HttpComponentsClientHttpRequestFactory(myHttpClient(properties)))
.rootUri(properties.getRootUri())
.build();
}
- Wait for many to complete
CompletableFuture.allOf(futures.toArray(new CompletableFuture[futures.size()]))