Issue
I'm using Spring Boot v2.7.0 and Spring Cloud stack to perform interservice communication. I saw few solutions like: https://github.com/spring-cloud/spring-cloud-gateway/issues/2091, but none of them worked for me.
I've updated code here: https://github.com/javaHelper/Spring-Boot-Microservices-sai-youtube/tree/main/3.Using-service-discovery
Error:
2022-06-10 09:31:26.600 ERROR 9506 --- [nio-8090-exec-1] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.web.reactive.function.client.WebClientRequestException: Failed to resolve 'inventory-service' after 4 queries ; nested exception is java.net.UnknownHostException: Failed to resolve 'inventory-service' after 4 queries ] with root cause
java.net.UnknownHostException: Failed to resolve 'inventory-service' after 4 queries
at io.netty.resolver.dns.DnsResolveContext.finishResolve(DnsResolveContext.java:1047) ~[netty-resolver-dns-4.1.77.Final.jar:4.1.77.Final]
at io.netty.resolver.dns.DnsResolveContext.tryToFinishResolve(DnsResolveContext.java:1000) ~[netty-resolver-dns-4.1.77.Final.jar:4.1.77.Final]
at io.netty.resolver.dns.DnsResolveContext.query(DnsResolveContext.java:418) ~[netty-resolver-dns-4.1.77.Final.jar:4.1.77.Final]
at io.netty.resolver.dns.DnsResolveContext.onResponse(DnsResolveContext.java:629) ~[netty-resolver-dns-4.1.77.Final.jar:4.1.77.Final]
at io.netty.resolver.dns.DnsResolveContext.access$400(DnsResolveContext.java:66) ~[netty-resolver-dns-4.1.77.Final.jar:4.1.77.Final]
at io.netty.resolver.dns.DnsResolveContext$2.operationComplete(DnsResolveContext.java:462) ~[netty-resolver-dns-4.1.77.Final.jar:4.1.77.Final]
at io.netty.util.concurrent.DefaultPromise.notifyListener0(DefaultPromise.java:578) ~[netty-common-4.1.77.Final.jar:4.1.77.Final]
at io.netty.util.concurrent.DefaultPromise.notifyListeners0(DefaultPromise.java:571) ~[netty-common-4.1.77.Final.jar:4.1.77.Final]
at io.netty.util.concurrent.DefaultPromise.notifyListenersNow(DefaultPromise.java:550) ~[netty-common-4.1.77.Final.jar:4.1.77.Final]
at io.netty.util.concurrent.DefaultPromise.notifyListeners(DefaultPromise.java:491) ~[netty-common-4.1.77.Final.jar:4.1.77.Final]
at io.netty.util.concurrent.DefaultPromise.setValue0(DefaultPromise.java:616) ~[netty-common-4.1.77.Final.jar:4.1.77.Final]
at io.netty.util.concurrent.DefaultPromise.setSuccess0(DefaultPromise.java:605) ~[netty-common-4.1.77.Final.jar:4.1.77.Final]
at io.netty.util.concurrent.DefaultPromise.trySuccess(DefaultPromise.java:104) ~[netty-common-4.1.77.Final.jar:4.1.77.Final]
at io.netty.resolver.dns.DnsQueryContext.trySuccess(DnsQueryContext.java:216) ~[netty-resolver-dns-4.1.77.Final.jar:4.1.77.Final]
at io.netty.resolver.dns.DnsQueryContext.finish(DnsQueryContext.java:208) ~[netty-resolver-dns-4.1.77.Final.jar:4.1.77.Final]
at io.netty.resolver.dns.DnsNameResolver$DnsResponseHandler.channelRead(DnsNameResolver.java:1314) ~[netty-resolver-dns-4.1.77.Final.jar:4.1.77.Final]
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379) ~[netty-transport-4.1.77.Final.jar:4.1.77.Final]
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365) ~[netty-transport-4.1.77.Final.jar:4.1.77.Final]
at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357) ~[netty-transport-4.1.77.Final.jar:4.1.77.Final]
at io.netty.handler.codec.MessageToMessageDecoder.channelRead(MessageToMessageDecoder.java:103) ~[netty-codec-4.1.77.Final.jar:4.1.77.Final]
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379) ~[netty-transport-4.1.77.Final.jar:4.1.77.Final]
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365) ~[netty-transport-4.1.77.Final.jar:4.1.77.Final]
at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:357) ~[netty-transport-4.1.77.Final.jar:4.1.77.Final]
at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1410) ~[netty-transport-4.1.77.Final.jar:4.1.77.Final]
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:379) ~[netty-transport-4.1.77.Final.jar:4.1.77.Final]
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:365) ~[netty-transport-4.1.77.Final.jar:4.1.77.Final]
at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:919) ~[netty-transport-4.1.77.Final.jar:4.1.77.Final]
at io.netty.channel.nio.AbstractNioMessageChannel$NioMessageUnsafe.read(AbstractNioMessageChannel.java:97) ~[netty-transport-4.1.77.Final.jar:4.1.77.Final]
at io.netty.channel.nio.NioEventLoop.processSelectedKey(NioEventLoop.java:722) ~[netty-transport-4.1.77.Final.jar:4.1.77.Final]
at io.netty.channel.nio.NioEventLoop.processSelectedKeysOptimized(NioEventLoop.java:658) ~[netty-transport-4.1.77.Final.jar:4.1.77.Final]
at io.netty.channel.nio.NioEventLoop.processSelectedKeys(NioEventLoop.java:584) ~[netty-transport-4.1.77.Final.jar:4.1.77.Final]
at io.netty.channel.nio.NioEventLoop.run(NioEventLoop.java:496) ~[netty-transport-4.1.77.Final.jar:4.1.77.Final]
at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:995) ~[netty-common-4.1.77.Final.jar:4.1.77.Final]
at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74) ~[netty-common-4.1.77.Final.jar:4.1.77.Final]
at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30) ~[netty-common-4.1.77.Final.jar:4.1.77.Final]
at java.base/java.lang.Thread.run(Thread.java:834) ~[na:na]
Order-service calling to inventory-service
order-service
application.properties
spring.application.name=order-service
#MySQL DB
spring.datasource.url=jdbc:mysql://localhost:3306/order-service
spring.datasource.username=root
spring.datasource.password=Password
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.jpa.database-platform=org.hibernate.dialect.MySQL5InnoDBDialect
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true
spring.sql.init.mode=always
spring.jpa.properties.hibernate.format_sql=true
server.port=8090
#Eureka
eureka.instance.prefer-ip-address=true
eureka.instance.hostname=localhost
eureka.client.serviceUrl.defaultZone=http://localhost:8761/eureka/
eureka.client.register-with-eureka=true
eureka.client.fetch-registry=true
spring.cloud.discovery.enabled=true
Calling Logic
private static final String INVENTORY_SERVICE_URI = "http://inventory-service/api/inventory";
public void placeOrder(OrderRequest orderRequest) {
log.info("OrderService | placeOrder method called ");
List<OrderLineItems> orderLineItems = orderRequest.getOrderLineItemsDtoList()
.stream()
.map(this::mapToDto)
.collect(Collectors.toList());
Order order = new Order();
order.setOrderNumber(UUID.randomUUID().toString());
order.setOrderLineItemsList(orderLineItems);
List<String> skuCodes = order.getOrderLineItemsList()
.stream()
.map(orderLineItem -> orderLineItem.getSkuCode())
.collect(Collectors.toList());
// Call Inventory Service, and place order if product is in stock
boolean allProductsInStock = webClient.get()
.uri(INVENTORY_SERVICE_URI, uriBuilder -> uriBuilder.queryParam("skuCode", skuCodes).build())
.retrieve()
.bodyToMono(InventoryResponse[].class)
.map(e -> Arrays.stream(e))
.block()
.allMatch(InventoryResponse::isInStock);
if(allProductsInStock){
orderRepository.save(order);
} else {
throw new IllegalArgumentException("Product is not in the stock, please try again later");
}
}
inventory-service
application.properties
spring.application.name=inventory-service
#MySQL DB
spring.datasource.url=jdbc:mysql://localhost:3306/inventory-service
spring.datasource.username=root
spring.datasource.password=Password
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.jpa.database-platform=org.hibernate.dialect.MySQL5InnoDBDialect
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true
spring.sql.init.mode=always
spring.jpa.properties.hibernate.format_sql=true
server.port=0
#Eureka
eureka.instance.prefer-ip-address=true
eureka.instance.hostname=localhost
eureka.instance.instance-id=${spring.application.name}:${random.int}
eureka.client.serviceUrl.defaultZone=http://localhost:8761/eureka/
eureka.client.register-with-eureka=true
eureka.client.fetch-registry=true
spring.cloud.discovery.enabled=true
As per solution suggested by @Chetan - If I have multiple instances of inventory-service up, then only this logic works, it doesn't work with single instance
Solution
For using the WebClient
with Discovery, declare following bean in configuration.
@Bean
@LoadBalanced
public WebClient.Builder loadBalancedWebClientBuilder() {
return WebClient.builder();
}
Autowire this in class where you are calling other service,
@Autowired
private WebClient.Builder webClientBuilder;
And, instead of webClient.get()
, use webClientBuilder.build().get()
Answered By - Chetan Ahirrao
Answer Checked By - Senaida (JavaFixing Volunteer)