Cannot disable Spring Cloud Config via spring.cloud.config.enabled:false - spring

Let me preface this by saying that I'm not using Spring Cloud Config directly, it is transitive via Spring Cloud Hystrix starter.
When only using #EnableHystrix, Spring Cloud also tries to locate a configuration server, expectedly unsuccessfully, since I'm not using one. The application works just fine, as far as I can tell, but the problem is in the status checks. Health shows DOWN because there is no config server.
Browsing the source of the project, I'd expect spring.cloud.config.enabled=false to disable this functionality chain, however this is not what I'm seeing.
After upgrading to 1.0.0.RC1 (which adds this property) and using #EnableCircuitBreaker:
{
status: "DOWN",
discovery: {
status: "DOWN",
discoveryClient: {
status: "DOWN",
error: "org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [org.springframework.cloud.client.discovery.DiscoveryClient] is defined"
}
},
diskSpace: {
status: "UP",
free: 358479622144,
threshold: 10485760
},
hystrix: {
status: "UP"
},
configServer: {
status: "DOWN",
error: "org.springframework.web.client.ResourceAccessException: I/O error on GET request for "http: //localhost: 8888/bootstrap/default/master":Connection refused: connect; nested exception is java.net.ConnectException: Connection refused: connect"
}
}
After checking the configprops endpoint, it seems that my properties are being overridden. Note that the parent has the configClient enabled.
parent: {
configClientProperties: {
prefix: "spring.cloud.config",
properties: {
password: null,
discovery: {
enabled: false,
serviceId: "CONFIGSERVER"
},
name: "bootstrap",
label: "master",
env: "default",
uri: "http://localhost:8888",
enabled: true,
failFast: false,
username: null
}
}
},
configClientProperties: {
prefix: "spring.cloud.config",
properties: {
password: null,
discovery: {
enabled: false,
serviceId: "CONFIGSERVER"
},
name: "bootstrap",
label: "master",
env: "default",
uri: "http://localhost:8888",
enabled: false,
failFast: false,
username: null
}
}
Any direction would be appreciated, if it seems I'm not doing this correctly.

The config server is needed during bootstrap, and that's where the parent property sources come from. It looks like all you need to do is move your spring.cloud.config.enabled property to bootstrap.yml (or .properties).

You can put a bootstrap properties or yml to your resource direcotry
or your applications directory and add
spring.cloud.config.enabled=false. OR
You can disable spring cloud config server client by adding an environment variable: SPRING_CLOUD_CONFIG_ENABLED=false OR
Config server client can be disabled by adding a parameter to your app, if you pass the args to parameters to SpringApplication.run:
public static void main(String[] args) throws Exception {
SpringApplication.run(YourApplication.class, args);
}
and start the app by:
java -jar yourapplication.jar --spring.cloud.config.enabled=false

I had the same problem, I wanted to have the config server disabled (as we do not need it so far) but the property mentioned above is not correct for RC1 at least.
spring.cloud.enabled
Should be:
spring.cloud.config.enabled

I tried all of the above changes and still config client never stopped somehow.
The only way I was able to disable it by using following exclusion in my project's pom.xml file.
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-client</artifactId>
</exclusion>
</exclusions>
</dependency>

Regarding the discovery service followup (looks like no other posts on that), setting spring.cloud.config.discovery.enabled: false worked for me, but only if it was set in bootstrap(yml/properties) and if I removed the #EnableDiscoveryClient annotation from my Application class. I guess this means one cannot use that annotation for any service where discovery will not be used at times.

None of those has helped me, I needed to disable spring cloud client bootstrap from server for my integrations tests, so whoever faces the same issue may use what as helped me:
#ComponentScan(excludeFilters = {
#ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, value = PropertySourceBootstrapConfiguration.class),
})
public class TestApplication {
}
and than annotate your tests with:
#SpringBootTest(classes = TestApplication.class)
class SomeIntegrationTest {}

Related

Centralized Swagger/OpenAPI UI for all the different microservices on a single swagger URL i.e accessing all the URLs through one

I have around 16 microservices built in Spring Boot that communicate with each other and each microservice is having multiple APIs in it. I have configured OpenAPI for each of the microservices. So it gives me 16 "swagger-ui" URLs.
I wonder how can I centralize all Swagger URLs on one single page; I want only 1 swagger URL to access all the 16 microservices.
I have gone through the following as well but didn't get any solution
Centralize Swagger at one place for All microservices
Please guide me to the best way to achieve it.
I found a working solution on GitHub. It works with Spring Cloud Discovery but can be easily adapted to other discovery solutions. The basic idea is to generate for Swagger the list of URLs pointing to the OpenAPI JSON files.
The result is similar to one in the #shadyx's answer, but the list is generated dynamically
#RestController
public class SwaggerUiConfig {
#Autowired
private DiscoveryClient discoveryClient;
#GetMapping("/swagger-config.json")
public Map<String, Object> swaggerConfig() {
List<SwaggerUrl> urls = new LinkedList<>();
discoveryClient.getServices().forEach(serviceName ->
discoveryClient.getInstances(serviceName).forEach(serviceInstance ->
urls.add(new SwaggerUrl(serviceName, serviceInstance.getUri() + "/v3/api-docs"))
)
);
return Map.of("urls", urls);
}
}
application.yaml
springdoc:
swagger-ui:
configUrl: "/swagger-config.json"
Sample of the generated swagger-config.json
{
"urls": [
{
"url": "http://localhost:8088/v3/api-docs/users",
"name": "users"
},
{
"url": "http://localhost:8088/v3/api-docs/stores",
"name": "stores"
}
]
}
If all you µservices use the same host, you can create a Spring Boot µservice which acts as a openAPI gateway using spring-doc. (If they are not using the same host, you will have to deal with CORS issues)
First, add the following dependencies to your new gateway µservice
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-ui</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
Then, add the below configuration
springdoc:
api-docs:
enabled: true
swagger-ui:
configUrl: ${server.servlet.contextPath}/v3/api-docs/swagger-config
url: ${server.servlet.contextPath}/v3/api-docs
urls:
- name: api-customer
url: /customer/v3/api-docs
- name: api-cart
url: /cart/v3/api-docs
- name: api-product
url: /product/v3/api-docs
For each µservice you have to fill in the url property with is the url to the openAPI definition at json or yaml format.
For springdoc library which uses openAPI 3.0 the default url is /${µServiceContextPath}/v3/api-docs
For springfox library which uses openAPI 2.0 the default url is /${µServiceContextPath}/v2/api-docs
Finally access to /${contextPath}/swagger-ui.html and you will be able to select an openAPI

management endpoints throwing GroovyCastException when served on different port

I have the following health management endpoints in my application.yml file
management:
endpoints:
health:
sensitive: false
web:
base-path: /
and I have an interceptor with the following code
class TestInterceptor {
TestInterceptor() {
matchAll()
}
boolean before() {
if (request.forwardURI?.endsWith('.json')) {
// ... some code
return false
}
true
}
}
this is working great. The application is working on 8080.
But as soon as I change the port of management endpoints (so that health check is served on a different port), the following code
management:
server:
port: 8989
endpoints:
health:
sensitive: false
web:
base-path: /
Ref# https://docs.spring.io/spring-boot/docs/2.1.7.RELEASE/reference/html/production-ready-monitoring.html
then the application is throwing a cast exception when accessing the request object in the interceptor
org.springframework.web.util.NestedServletException: Request
processing failed; nested exception is
org.codehaus.groovy.runtime.typehandling.GroovyCastException: Cannot
cast object 'Request(GET
//localhost:8989/testApp/health)#68117e64' with class
'org.springframework.web.context.request.ServletRequestAttributes' to
class 'org.grails.web.servlet.mvc.GrailsWebRequest'
any suggestion to fix the issue.
(Grails 4.0.12, Groovy 2.5.14 and Java 11)
Upgrade to Grails 5. The above functionality working fine with that.

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.

Why X-RateLimit-Remaining -1 in response header while using spring cloud api gateway ratelimit with redis?

I implemented ratelimit with redis in my spring cloud api gateway. Here is part of application.yml:
spring:
cloud:
gateway:
httpclient:
ssl:
useInsecureTrustManager: true
discovery:
locator:
enabled: true
routes:
- id: test-rest-service
uri: lb://test-rest-service
predicates:
- Path=/test/**
filters:
- RewritePath=/test/(?<path>.*), /$\{path}
- name: RequestRateLimiter
args:
key-resolver: "#{#userRemoteAddressResolver}"
redis-rate-limiter.replenishRate: 2
redis-rate-limiter.burstCapacity: 3
I called a GET API via postman and checked response header.
X-RateLimit-Remaining -1
X-RateLimit-Burst-Capacity 3
X-RateLimit-Replenish-Rate 2
The rate limit is not working. Why am I getting negative value for X-RateLimit-Remaining? What does it mean? How do I fix it?
This happened to me because there was no Redis instance launched. You have two options:
1) Download and run a Redis instance using docker:
docker run --name redis -d redis
2) You can use in testing an Embedded Redis Server as it is explained in the following article by adding the maven dependency:
<dependency>
<groupId>it.ozimov</groupId>
<artifactId>embedded-redis</artifactId>
<version>0.7.2</version>
<scope>test</scope>
</dependency>
And including the following snippet:
#TestConfiguration
public class TestRedisConfiguration {
private RedisServer redisServer;
public TestRedisConfiguration() {
this.redisServer = new RedisServer(6379);
}
#PostConstruct
public void postConstruct() {
redisServer.start();
}
#PreDestroy
public void preDestroy() {
redisServer.stop();
}
}
I faced the same issue recently. In my case, there was an older version of Redis installed which caused X-RateLimit-Remaining to be set to -1 constantly.
redis-cli shutdown

#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

Resources