spring-projects / spring-framework

Spring Framework
https://spring.io/projects/spring-framework
Apache License 2.0
56.25k stars 37.98k forks source link

UnresolvedAddressException when using webClient.get() to get from a discovery eureka client #32439

Closed Ivanbattochio closed 6 months ago

Ivanbattochio commented 6 months ago

Hi, I'd like to know if anyone can help me.

I have a setup of microservices registering to a netflix discovery server, and I'm having problems using webClient to communicate with the servers using their instance names. When trying to request using RestTemplate it works as expected.

The entire project source code is inside this repo!

InventoryResponse[] inventoryResponses = webClient.get() .uri("http://inventory-service/api/inventory", uriBuilder -> uriBuilder.queryParam("skuCode", skuCodes).build()) .retrieve() .bodyToMono(InventoryResponse[].class) .block();

This is how I'm building and sending the request from the order-service to the inventory-service and this is the error stacktrace:

java.nio.channels.UnresolvedAddressException: null at java.base/sun.nio.ch.Net.checkAddress(Net.java:137) ~[na:na] at java.base/sun.nio.ch.Net.checkAddress(Net.java:145) ~[na:na] at java.base/sun.nio.ch.SocketChannelImpl.checkRemote(SocketChannelImpl.java:842) ~[na:na] at java.base/sun.nio.ch.SocketChannelImpl.connect(SocketChannelImpl.java:865) ~[na:na] at java.net.http/jdk.internal.net.http.PlainHttpConnection.lambda$connectAsync$1(PlainHttpConnection.java:210) ~[java.net.http:na] at java.base/java.security.AccessController.doPrivileged(AccessController.java:571) ~[na:na] at java.net.http/jdk.internal.net.http.PlainHttpConnection.connectAsync(PlainHttpConnection.java:212) ~[java.net.http:na] at java.net.http/jdk.internal.net.http.Http1Exchange.sendHeadersAsync(Http1Exchange.java:312) ~[java.net.http:na] at java.net.http/jdk.internal.net.http.Exchange.lambda$responseAsyncImpl0$8(Exchange.java:567) ~[java.net.http:na] at java.net.http/jdk.internal.net.http.Exchange.checkFor407(Exchange.java:447) ~[java.net.http:na] at java.net.http/jdk.internal.net.http.Exchange.lambda$responseAsyncImpl0$9(Exchange.java:571) ~[java.net.http:na] at java.base/java.util.concurrent.CompletableFuture.uniHandle(CompletableFuture.java:934) ~[na:na] at java.base/java.util.concurrent.CompletableFuture.uniHandleStage(CompletableFuture.java:950) ~[na:na] at java.base/java.util.concurrent.CompletableFuture.handle(CompletableFuture.java:2372) ~[na:na] at java.net.http/jdk.internal.net.http.Exchange.responseAsyncImpl0(Exchange.java:571) ~[java.net.http:na] at java.net.http/jdk.internal.net.http.Exchange.responseAsyncImpl(Exchange.java:423) ~[java.net.http:na] at java.net.http/jdk.internal.net.http.Exchange.responseAsync(Exchange.java:415) ~[java.net.http:na] at java.net.http/jdk.internal.net.http.MultiExchange.responseAsyncImpl(MultiExchange.java:413) ~[java.net.http:na] at java.net.http/jdk.internal.net.http.MultiExchange.lambda$responseAsyncImpl$7(MultiExchange.java:454) ~[java.net.http:na] at java.base/java.util.concurrent.CompletableFuture.uniHandle(CompletableFuture.java:934) ~[na:na] at java.base/java.util.concurrent.CompletableFuture.uniHandleStage(CompletableFuture.java:950) ~[na:na] at java.base/java.util.concurrent.CompletableFuture.handle(CompletableFuture.java:2372) ~[na:na] at java.net.http/jdk.internal.net.http.MultiExchange.responseAsyncImpl(MultiExchange.java:444) ~[java.net.http:na] at java.net.http/jdk.internal.net.http.MultiExchange.lambda$responseAsync0$2(MultiExchange.java:346) ~[java.net.http:na] at java.base/java.util.concurrent.CompletableFuture$UniCompose.tryFire(CompletableFuture.java:1150) ~[na:na] at java.base/java.util.concurrent.CompletableFuture.postComplete(CompletableFuture.java:510) ~[na:na] at java.base/java.util.concurrent.CompletableFuture$AsyncSupply.run$$$capture(CompletableFuture.java:1773) ~[na:na] at java.base/java.util.concurrent.CompletableFuture$AsyncSupply.run(CompletableFuture.java) ~[na:na] at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1144) ~[na:na] at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:642) ~[na:na] at java.base/java.lang.Thread.run(Thread.java:1583) ~[na:na]

This is the inventory-service application.properties

spring.datasource.driver-class-name=org.postgresql.Driver spring.datasource.url=jdbc:postgresql://localhost:5432/inventoryservice spring.datasource.username=postgres spring.datasource.password=admin spring.jpa.hibernate.ddl-auto=create-drop spring.jpa.database-platform=org.hibernate.dialect.PostgreSQLDialect server.port=8082 eureka.client.service-url.default-zone=http://localhost:8761/eureka spring.application.name=inventory-service

This is the inventory-service build.gradle file

`plugins { id 'java' id 'org.springframework.boot' version '3.2.3' id 'io.spring.dependency-management' version '1.1.4' }

group = 'com.example.microservices' version = '0.0.1-SNAPSHOT'

java { sourceCompatibility = '21' }

ext { set('springCloudVersion', "2023.0.0") }

configurations { compileOnly { extendsFrom annotationProcessor } }

repositories { mavenCentral() }

dependencies { implementation 'org.springframework.boot:spring-boot-starter-data-jpa' implementation 'org.springframework.boot:spring-boot-starter-web' implementation 'org.springframework.cloud:spring-cloud-starter-netflix-eureka-client' compileOnly 'org.projectlombok:lombok' runtimeOnly 'org.postgresql:postgresql' annotationProcessor 'org.projectlombok:lombok' testImplementation 'org.springframework.boot:spring-boot-starter-test' }

dependencyManagement { imports { mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}" } }

tasks.named('test') { useJUnitPlatform() } `

This is the order-service application.properties

spring.datasource.driver-class-name=org.postgresql.Driver spring.datasource.url=jdbc:postgresql://localhost:5432/postgres spring.datasource.username=postgres spring.datasource.password=admin spring.jpa.hibernate.ddl-auto=update spring.jpa.database-platform=org.hibernate.dialect.PostgreSQLDialect server.port=8081 eureka.client.service-url.default-zone=http://localhost:8761/eureka spring.application.name=order-service

This is the order service build.gradle

`plugins { id 'java' id 'org.springframework.boot' version '3.2.3' id 'io.spring.dependency-management' version '1.1.4' }

group = 'com.example.springmicroservices' version = '0.0.1-SNAPSHOT'

java { sourceCompatibility = '21' }

ext { set('springCloudVersion', "2023.0.0") }

configurations { compileOnly { extendsFrom annotationProcessor } }

repositories { mavenCentral() }

dependencies { implementation 'org.springframework.boot:spring-boot-starter-data-jpa' implementation 'org.springframework.boot:spring-boot-starter-web' implementation 'org.springframework.cloud:spring-cloud-starter-netflix-eureka-client' implementation 'org.springframework:spring-webflux' implementation 'jakarta.validation:jakarta.validation-api' compileOnly 'org.projectlombok:lombok' runtimeOnly 'org.postgresql:postgresql' runtimeOnly 'com.mysql:mysql-connector-j' annotationProcessor 'org.projectlombok:lombok' testImplementation 'org.springframework.boot:spring-boot-starter-test' }

dependencyManagement { imports { mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}" } }

tasks.named('test') { useJUnitPlatform() } ` This is the discovery-service application.properties

spring.application.name=discovery-service eureka.instance.hostname=localhost eureka.client.register-with-eureka=false eureka.client.fetch-registry=false server.port=8761 eureka.client.service-url.default-zone=http://localhost:8761/eureka

This is the discovery-service build.gradle

`plugins { id 'java' id 'org.springframework.boot' version '3.2.3' id 'io.spring.dependency-management' version '1.1.4' }

group = 'com.example.microservices' version = '0.0.1-SNAPSHOT'

java { sourceCompatibility = '21' }

repositories { mavenCentral() }

ext { set('springCloudVersion', "2023.0.0") }

dependencies { implementation 'org.springframework.cloud:spring-cloud-starter-netflix-eureka-server' testImplementation 'org.springframework.boot:spring-boot-starter-test' }

dependencyManagement { imports { mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}" } }

tasks.named('test') { useJUnitPlatform() } `

I'm using Java 21 and spring boot 3.2.3.

When debbuging the request i found where the error gets thrown:

image

I've tried to provide all the information i think anyone would need to replicate the problem, if i let something slip by please let me know so that i can provide more context!

Thanks!

bclozel commented 6 months ago

Can you open this issue against https://github.com/spring-cloud/spring-cloud-netflix/issues - this feature is not implemented in Spring Framework.

Thanks!

Ivanbattochio commented 6 months ago

Isn't the Webclient implementation from spring framework repository? Hence the org.springframework:spring-webflux import and the class file.

I believe the error is originating from the spring framework's webclient implementation because performing the get request using the RestTemplate implementation works as expected and i am able to communicate with the eureka client using it's instance name.

I could be missing something here, but as to my knowledge I still believe this is spring framework's issue instead of spring cloud's.

bclozel commented 6 months ago

Yes you are right WebClient is implemented in Spring Framework.

Reading your issue description, it looks like the client cannot resolve the remote endpoint address because the service name was not replaced by the actual host name of the service instance. Isn't this feature implemented in Spring Cloud? We can reopen this issue if you manage to simplify the repro project and reproduce without Spring Cloud being there at all. Please advise.