Spring Test with #RestClientTest and #FeignAutoConfiguration not working - spring

I have the following configuration:
#SpringBootApplication
#EnableFeignClients
public class Application {
#FeignClient(name = "theclient")
public interface TheClient {
...
theclient:
ribbon:
listOfServers: http://server:8080
And have the following test:
#RunWith(SpringRunner.class)
#RestClientTest(TheClient.class)
#ImportAutoConfiguration(
classes = {
RibbonAutoConfiguration.class,
FeignRibbonClientAutoConfiguration.class,
FeignAutoConfiguration.class
}
)
public class TheClientTest {
...
The error I get is Caused by: com.netflix.client.ClientException: Load balancer does not have available server for client: theclient as if the application.yaml was not read. If I add, however, a config.properties file with the theclient.ribbon.listOfServers=http://server:8080, the tests works and passes.
With #TestPropertySource("classpath:application.yaml") I see in the logs propertySourceLocations = '{classpath:application.yaml}' but I will get the same error.
I also tried to disable ribbon, by adding:
spring:
cloud:
loadbalancer:
ribbon:
enable: false
but it won't work.
I appreciate your input and help.

Related

Load balancer does not contain an instance for the service

I want to use Eureka client with spring-cloud-starter-loadbalancer. But when I added configuration I get error. When I remove #LoadBalancerClient(name = "mail-service", configuration = LoadBalancerConfiguration.class) and LoadBalancerConfiguration class configuration it's working fine. I tried this code:
#FeignClient(name = "mail-service")
#LoadBalancerClient(name = "mail-service", configuration = LoadBalancerConfiguration.class)
public interface EmailClient {
#RequestMapping(method = RequestMethod.POST, value = "/engine/emails/register")
void setUserRegistration(CreateUserDTO createUserDTO);
}
#Configuration
public class LoadBalancerConfiguration {
#Bean
public ServiceInstanceListSupplier discoveryClientServiceInstanceListSupplier(ConfigurableApplicationContext context) {
return ServiceInstanceListSupplier.builder()
.withBlockingDiscoveryClient()
.withSameInstancePreference()
.withHealthChecks()
.build(context);
}
}
application.yml:
feign:
client:
config:
default:
connectTimeout: 5000
readTimeout: 5000
loggerLevel: basic
eureka:
client:
serviceUrl:
defaultZone: ${EUREKA_URI:http://localhost:8761/eureka}
fetchRegistry: true
healthcheck:
enabled: true
instance:
preferIpAddress: true
leaseRenewalIntervalInSeconds: 10
POM.xml dependencies
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<dependency>
<groupId>com.netflix.eureka</groupId>
<artifactId>eureka-core</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-loadbalancer</artifactId>
</dependency>
I get this error when I have only one target service.
[503] during [POST] to [http://mail-service/engine/emails/register] [EmailClient#setUserRegistration(CreateUserDTO)]: [Load balancer does not contain an instance for the service mail-service]
I use Release Train Version: 2020.0.3
Do you know what could be the problem?
Any application using load balancer should follow the below order
Start the Eureka Server
Start the instances of the Service one by one which have dependency
Any application relies on information from a service registry (i.e. Eureka). Until the application is registered with it's instances by the serviceId , the Eureka server will not be able to pick that instance while load-balancing.
In the code you have shared, the bean ServiceInstanceListSupplier is created in the configuration class along with the health checks ( .withHealthChecks() ). This is causing the application to fail as service has not been registered yet.
Also, the LoadBalancer config should not be in a #Configuration-annotated class instead, it should be a class passed for config via #LoadBalancerClient or #LoadBalancerClients annotation, as described here.
The only bean you need to instantiate is the ServiceInstanceListSupplier (if you add spring-cloud-starter-loadbalancer, LoadBalancerClientFactory etc. will be instantiated by the starter itself).
So your LoadBalancer configuration class should look like code below.
It should not be in the #Configuration class:
public class LoadBalancerConfiguration {
#Bean
public ServiceInstanceListSupplier instanceSupplier(ConfigurableApplicationContext context) {
return ServiceInstanceListSupplier.builder()
.withDiscoveryClient()
.withHealthChecks()
.build(context);
}
}
As explained in this link, the actual #Configuration class , will have the following annotation: #LoadBalancerClients(defaultConfiguration = LoadBalancerConfiguration .class).
Then, if you enable health-checks in the instances, it should work without any issues.

Spring Cloud Server Config only refreshing application.yml and not other properties files

I have a bunch of YAML and properties files in my Spring Boot project :
- application.yml
- application-dev.yml
- spring-integration.properties
- common.properties
- test.yml
- ...
I'm trying to understand how can I implement Spring Cloud Server Config for my project and I have a hard time figuring out why Spring Cloud Server Config is not picking up any of the changes on any of my other files (but application.yml & application-dev.yml).
For example, in my spring-integration.properties, after changing this value :
start.task.rate=500000
And POST to /management/refresh, nothing is detected by the actuator and I get an {} response.
However, when I change my datasource in application.yml, I get the expected result :
[
"spring.datasource.url"
]
Here is my Application.java :
#SpringBootApplication
#EnableConfigServer
#ImportResource("classpath:integration-config.xml")
#ConfigurationProperties
#EnableSwagger2
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
And my bootstrap.yml :
spring:
application:
name: ${appName:tasky}
profiles:
include:
- native
cloud:
config:
failFast: true
server:
bootstrap: true
prefix: /config
native:
search-locations: file:///C:/workspace/tasky/config/
I'm using :
<spring-cloud.version>Edgware.SR1</spring-cloud.version>
<spring-boot-version>1.5.9.RELEASE</spring-boot-version>
From what I could gathered, on /refresh Spring Actuator (?) is only picking up properties files starting with ${appName} or application, in my case I had to regroup all my properties files under one file named : tasky-{profile}.properties.

#FeignClient always timeout when using eureka service id

I have a spring-boot app which uses Declarative Feign Client
#ComponentScan
#EnableFeignClients
#EnableCircuitBreaker
#EnableDiscoveryClient
#EnableZuulProxy
#FeignClient(name = "${service-registry-name}", fallbackFactory = MyFallbackFactory.class, configuration = CommonFeignConfiguration.class)
public interface MyClient {
#RequestMapping(method = RequestMethod.GET, path = "/test/reference/data")
HttpEntity<String> getAllData();}
I have following application.yml
feign:
okhttp:
enabled: true
feign:
hystrix:
enabled: true
hystrix:
command:
MyClient#getAllData():
execution:
isolation:
thread:
timeoutInMilliseconds: 30000
hystrix:
command:
default:
execution:
timeout:
enabled: false
logging:
level:
project:
user:
MyClient: DEBUG
feign:
client:
config:
feign-name:
requestInterceptors: com.test.MyRequestHeaderProcessor
This spring-boot app works perfectly fine and when I debug the I could see that the timeout value of 30000 is properly applied.
The trouble starts when I use this code NOT as a standalone spring boot app but as a dependency jar into another project.
At this time, the timeout is always 1000, which is the default. I managed to override this as well. But despite of that, i get HystrixRunTimeException, Timeout with null.
I have feign.hystrix.enabled=true.
If I use feign.hystrix.enabled=false, I can see that my request doesnt time out but then the Fallback mechanism fails to work.
But, when I add URL attribute in FeignClient it works fine and does NOT timeout.I cannot rely on the URL attribute as this is coming from the cloud foundry service URL which can change.
Add below properties in Application.yml file.
feign:
client:
config:
default:
connectTimeout: 80000000
readTimeout: 80000000

Spring Cloud Config Server issue - Configuring multiple sources native and jdbc

I want to connect to multiple repositories i.e native (file system) and jdbc in spring cloud config. I created a spring cloud config server with below details
application.properties
server.port=8888
spring.profiles.include=native,jdbc
spring.cloud.config.server.native.search-locations=classpath:/config,classpath:/app1, classpath:/app2,classpath:/ep
encrypt.key=abcdef
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/configuration?useSSL=false
spring.cloud.config.server.jdbc.sql=SELECT properties.key, properties.value from PROPERTIES where APPLICATION=? and PROFILE=? and LABEL=?
spring.datasource.username=root
spring.datasource.password=root
spring.cloud.config.server.native.order=1
spring.cloud.config.server.jdbc.order=2
Irrespective of priority order it always fetches information from jdbc and not from native.
I tried adding the last 2 properties for order to bootstrap.properties still same behavior.
Am is missing anything ? Is my configuration correct ? Please suggest
in spring boostrap.yml loaded before application.yml so you declare server port,config search location and active profile configuration is good approach for this stack,so keep it simple boostrap.yml also spring cloud default profile is native
and in application-"profile".yml is have environment and other configuration properties
and your boostrap.yml or properites like that
server:
port: 8888
spring:
application:
name: appName
profiles:
active: native,jdbc
cloud:
config:
server:
native:
order: 1
searchLocations: classpath:/config,classpath:/app1, classpath:/app2,classpath:/ep
and create applicaiton-jdbc.properties or yml file in same layer in boostrap.yml or properties and declare jdbc properties
spring:
datasource:
driver-class-name: com.mysql.jdbc.Driver
url: 'jdbc:mysql://localhost:3306/configuration?useSSL=false'
cloud:
config:
server:
jdbc:
order: 2
sql: 'SELECT properties.key, properties.value from PROPERTIES where APPLICATION=? and PROFILE=? and LABEL=?'
username: root
password: root
and your config server configuration like this
#SpringBootApplication
#EnableConfigServer
#Import({JdbcEnvironmentRepository.class})
public class ConfigServer {
#ConfigurationProperties(prefix = "spring.datasource")
#Bean
public DataSource dataSource() {
return DataSourceBuilder.create().build();
}
#Bean
public JdbcTemplate jdbcTemplate() {
return new JdbcTemplate(dataSource());
}
public static void main(String[] arguments) {
SpringApplication.run(ConfigServer.class, arguments);
}
}

Spring Cloud : Zuul throwing "Load balancer does not have available server for client"

I am trying to get a simple microservice to register to the Eureka Server and then have Zuul proxy to the microservice. I got the microservice to register to the Eureka Server. However, whenever I bring the Zuul service up I see that it is not registering to the Eureka Server. Whenever I try to route to the microservice I get the below exception:
Load balancer does not have available server for client: microservice
I am not able to figure out where the issue is. Any help would be greatly appreciated
Below are my Spring Boot classes for each of these components.
Microservice Components
MicroserviceApplication.java
#SpringBootApplication(scanBasePackages = { "some.package.controller", "some.package.service" })
#EnableEurekaClient
#EnableJpaRepositories("some.package.repository")
#EntityScan("some.package.entity")
public class MicroserviceApplication {
public static void main(String[] args) {
SpringApplication.run(MicroserviceApplication.class, args);
}
}
bootstrap.yml
server:
port: 8090
info:
component: Core Services
spring:
application:
name: microservice
application.yml
eureka:
instance:
leaseRenewalIntervalInSeconds: 1
leaseExpirationDurationInSeconds: 2
client:
serviceUrl:
defaultZone: http://localhost:8761/eureka/
healthcheck:
enabled: true
lease:
duration: 5
#some other logging and jpa properties below
Eureka Server Components
EurekaServerApplication.java
#SpringBootApplication
#EnableEurekaServer
public class EurekaServerApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaServerApplication.class, args);
}
}
bootstrap.yml
spring:
application:
name: discovery-server
application.yml
server:
port: 8761
info:
component: Discovery Server
eureka:
client:
registerWithEureka: false
fetchRegistry: false
serviceUrl:
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
instance:
hostname: localhost
server:
waitTimeInMsWhenSyncEmpty: 0
API Gateway Components
ZuulGatewayApplication.java
#SpringBootApplication
#EnableZuulProxy
public class ZuulGatewayApplication {
public static void main(String[] args) {
SpringApplication.run(ZuulGatewayApplication.class, args);
}
#Bean
public ZuulGatewayPreFilter gatewayPreFilter() {
return new ZuulGatewayPreFilter();
}
}
bootstrap.yml
spring:
application:
name: api-gateway
application.yml
server:
port: 8888
info:
component: API Gateway
eureka:
instance:
leaseRenewalIntervalInSeconds: 1
leaseExpirationDurationInSeconds: 2
client:
serviceUrl:
defaultZone: http://localhost:8761/eureka/
zuul:
routes:
microservice:
path: /microservice/**
serviceId: microservice
Adding #EnableEurekaClient solved this issue. The Zuul application is now discoverable in the Eureka server and is able to route requests.
I had the correct annotation on my Zuul application #EnableZuulClient but still got that error.
The problem was that I started all the applications (Eureka, Zuul and the third application) at the same time instead of doing this one by one. So they all needed time to register. After some time, the error was gone and I could reach my service through Zuul client.
Wrong spring.application.name might be the reason for this error other than
adding #EnableEurekaClient to the Zuul application class.

Resources