consul first bootstrap with spring cloud config - spring-boot

Iam using spring-cloud-config for centralized configuration and consul for service discovery. Like eureka first bootstrap - does spring support consul first bootstrap i.e on booting up a client service - I should look up the config server through consul. The otherway round works perfectly fine i.e - in config client bootstrap.properties - I provide the spring.cloud.config.uri=http://localhost:8888 which located the config server and pulls config from it. And in the config repository for my client application - I provide the consul config like :
spring.cloud.consul.host=localhost ,
spring.cloud.consul.port=8500
However, when i try to use consul first bootstrap I am unable to read the properties from config server.
Client Application (for consul first bootstrap):
pom.xml
<parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-parent</artifactId>
<!-- <version>Brixton.BUILD-SNAPSHOT</version> -->
<version>Brixton.M5</version>
<relativePath />
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-consul-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-consul-all</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zuul</artifactId>
</dependency>
</dependencies>
bootstrap.properties:
spring.application.name=demo
spring.cloud.config.failFast=true
spring.cloud.config.retry.maxAttempts=20
spring.cloud.config.retry.initialInterval=3000
spring.cloud.consul.host=localhost
spring.cloud.consul.port=8500
DemoApplication.java
#EnableDiscoveryClient
#EnableZuulProxy
#SpringBootApplication
public class DemoSleuthApplication {
public static void main(String[] args) {
SpringApplication.run(DemoSleuthApplication.class, args);
}
}
#RefreshScope
#RestController
class RestAPIController
{
#Value(value = "${server.port}")
String port;
#Value(value = "${message}")
String message;
#RequestMapping("/message")
public String welcome(){
String s = this.restTemplate.getForObject("http://localhost:"+this.port+"/message", String.class);
return this.message + s;
}
}
In the consul K/V store
folder structure config/demo
Key/Value : spring.cloud.config.uri=http://localhost:8888
Config server git repo: not adding the config server code for brevity
demo.properties
server.port=9080
message=test
Ideally when I implement the concept of consul first bootstrap - I am thinking consul should be started and the client should identify itself using the #EnableDiscoveryClient annotation and in the consul properties - find config server url , and fetch the config properties from the server configurations. But in my case, service is being discovered and registered in consul but i am not able to read properties from config server git repo.

It was done here. It is available in SNAPSHOTS and in RC2 which will come hopefully next week.

Giving my sample code here for benefit of others. I had to do a lot of tinkering with the properties file to get to this.
As answered by #spencergibb it is available in SNAPSHOT only for now.
This time i did not use any key value properties in consul.
config server code:
pom.xml:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-consul-all</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-consul-discovery</artifactId>
</dependency>
application.yml
spring:
profiles:
active: native
cloud:
config:
server:
native:
search-locations: file://${HOME}/properties
consul:
port: 8500
host: localhost
enabled: true
discovery:
enabled: true
register: true
service-name: server --registers in consul as server instead of config-server
hostname: localhost
server:
port: 8888
bootstrap.yml ::
spring:
application:
name: config-server
COnfigServerApplication.java
#EnableDiscoveryClient
#EnableConfigServer
#SpringBootApplication
public class SpringConfigServerApplication {
public static void main(String[] args) {
SpringApplication.run(SpringConfigServerApplication.class, args);
}
}
Client microservice: demo
<parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-parent</artifactId>
<version>Brixton.BUILD-SNAPSHOT</version>
<!-- <version>Brixton.M5</version> -->
<relativePath />
</parent>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-consul-all</artifactId>
</dependency>
bootstrap.properties
spring.application.name=demo-spring-cloud-sleuth
spring.cloud.config.failFast=true
spring.cloud.config.retry.maxAttempts=20
spring.cloud.config.retry.initialInterval=3000
spring.cloud.config.enabled=true
spring.cloud.config.discovery.enabled=true
spring.cloud.config.discovery.serviceId=config-server
spring.cloud.consul.discovery.hostName=localhost
spring.cloud.consul.discovery.register=true -- unless this is there, the service fails to register in consul.
git uri property file for client:
server.port=9082
message=message local
foo1=bar

Related

How to set up Spring Cloud Gateway application so it can use the Service Discovery of Spring Cloud Kubernetes?

I created two Spring Boot applications which both will be deployed in a Kubernetes cluster. One of those apps will act as a gateway and therefore uses Spring Cloud Gateway as a dependency. Also I want to integrate service discovery with Spring Cloud Kubernetes and that the gateway uses the service discovery to automatically generate corresponding routes. But when I expose the gateway application, which is running in an local Minikube cluster, and invoke the second app/service I get a 503 error with following message: Unable to find instance for ...-service
Currently I have installed following:
Minikube
VirtualBox
Docker Toolbox
I have created a Gradle project with two subprojects (gateway and another service). All projects will be build/deployed locally. The default Service Account has permission to read the Kubernetes API. After deployment of those services I expose the gateway service externally. In the gateway service I have some endpoints implemented, which
provide a list of all services in the cluster vie the DiscoveryClient.
on application layer invoke the other service based on the URI provided by the DiscoveryClient.
Everything seems to work but when I invoke the other service with URI/serviceId I get the 503 error...
Following Spring Cloud versions are used:
spring-cloud-starter-kubernetes 1.0.1.RELEASE
spring-cloud-starter-gateway 2.1.1.RELEASE
My demo app is available at https://github.com/nmaoez/spring-cloud-gateway-kubernetes and the README.md provides steps to get both services deployed in a local Minikube cluster. Also all available endpoints are shown. But the interessing part are the application.yaml and the application class of the gateway.
application.yaml:
spring:
application:
name: gateway-service
cloud:
gateway:
discovery:
locator:
enabled: true
lower-case-service-id: true
management:
endpoints:
web:
exposure:
include: '*'
GatewayApplication.java
#SpringBootApplication
#EnableDiscoveryClient
#RestController
public class GatewayApplication {
#Autowired
RestTemplate restTemplate;
#Autowired
private DiscoveryClient discoveryClient;
#GetMapping("/")
#ResponseBody
public String hello() {
return "GatewayApplication says hello!";
}
#GetMapping("/test")
#ResponseBody
public String invokeTestService() {
List<ServiceInstance> testServiceInstances = this.discoveryClient.getInstances("test-service");
return restTemplate.getForObject(testServiceInstances.get(0).getUri(), String.class);
}
#GetMapping("/services")
public List<String> services() {
return this.discoveryClient.getServices();
}
#GetMapping("/services/{serviceId}")
public List<ServiceInstance> servicesById(#PathVariable("serviceId") String serviceId) {
return this.discoveryClient.getInstances(serviceId);
}
#Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
public static void main(String[] args) {
SpringApplication.run(GatewayApplication.class, args);
}
}
I got it some how running after I overwrote the url-expression field in the gateway-service/application.yaml to
url-expression: "uri+'/'"
After that I got a correct response after I invoked gateway-uri/another-service/. But my wish is to not explicitly replace the default of lb://serviceid. How can I do that?
I expect that if I invoke another service in the cluster though the gateway, I get a 200 response and the correct answer based in the rest controller of the application.
You have to add the dependency to spring-cloud-starter-kubernetes-ribbon as well.
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-kubernetes-ribbon</artifactId>
<version>1.0.1.RELEASE</version>
</dependency>
Then it will work without any rewrites, just with the spring.cloud.gateway.discovery.locator.enabled: true
I just implemented an example application using Spring Cloud Gateway and Kubernetes, that works like a charm in Docker Desktop. And no extra nor funny configurations were needed.
If it may help this was my build.gradle:
plugins {
id 'org.springframework.boot' version '2.4.2'
id 'io.spring.dependency-management' version '1.0.11.RELEASE'
id 'java'
}
group = 'com.example'
version = '0.1.0-SNAPSHOT'
repositories {
mavenCentral()
maven { url 'https://repo.spring.io/milestone' }
}
ext {
set('springCloudVersion', "2020.0.0")
set('springCloudKubernetesVersion', '1.1.7.RELEASE')
set('springCloudVersion', '2020.0.0')
}
dependencies {
implementation 'org.springframework.boot:spring-boot-starter-actuator'
implementation 'org.springframework.cloud:spring-cloud-starter-gateway'
implementation 'org.springframework.cloud:spring-cloud-starter-sleuth'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
implementation "org.springframework.cloud:spring-cloud-starter-kubernetes:$springCloudKubernetesVersion"
implementation "org.springframework.cloud:spring-cloud-starter-kubernetes-config:$springCloudKubernetesVersion"
implementation "org.springframework.cloud:spring-cloud-starter-kubernetes-ribbon:$springCloudKubernetesVersion"
implementation "org.springframework.cloud:spring-cloud-starter-kubernetes-loadbalancer:$springCloudKubernetesVersion"
}
dependencyManagement {
imports {
mavenBom "org.springframework.cloud:spring-cloud-dependencies:$springCloudVersion"
}
}
test {
useJUnitPlatform()
}
This is the configuration from the application.yaml:
spring:
cloud:
gateway:
discovery:
locator:
enabled: true
And finally, the DiscoveryClient is enabled in the app:
#SpringBootApplication
#EnableDiscoveryClient // So services can be discovered
public class GatewayApplication {
public static void main(String[] args) {
SpringApplication.run(GatewayApplication.class, args);
}
}
Note as Jakub Kubrynski said, you must include the Ribbon dependency.
Also beware routing only works when the gateway is deployed to the K8s cluster. Outside it, it can see the K8s services but cannot route to them since they are using IP addresses in the K8s network.
I'm able to setup spring cloud gateway with discovery of spring-cloud-kubernetes dependency version 1.1.10.RELASE and Spring-boot: 2.5.7 Spring cloud gateway: 3.0.4
Pom file looks like below:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.7</version>
<relativePath/>
</parent>
<groupId>com.csg.cro.rccs</groupId>
<artifactId>api-gateway</artifactId>
<version>${revision}</version>
<name>RCC-APIGateway</name>
<description>Enable Proxy and verify user token project for Risk 360</description>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>11</java.version>
<spring-cloud.version>2020.0.3</spring-cloud.version>
<revision>21.7.0-SNAPSHOT</revision>
</properties>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-kubernetes</artifactId>
<version>1.1.10.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-kubernetes-config</artifactId>
<version>1.1.10.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-kubernetes-ribbon</artifactId>
<version>1.1.10.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-kubernetes-loadbalancer</artifactId>
<version>1.1.10.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>flatten-maven-plugin</artifactId>
<version>1.1.0</version>
<configuration>
<updatePomFile>true</updatePomFile>
<flattenMode>resolveCiFriendliesOnly</flattenMode>
</configuration>
<executions>
<execution>
<id>flatten</id>
<phase>process-resources</phase>
<goals>
<goal>flatten</goal>
</goals>
</execution>
<execution>
<id>flatten.clean</id>
<phase>clean</phase>
<goals>
<goal>clean</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
</project>
My discovery config look like:
spring:
application.name: gateway
cloud:
gateway:
discovery:
locator:
enabled: true
url-expression: "'http://'+serviceId+':'+getPort()"
lower-case-service-id: true

Resource leak detected when using spring-data-redis on cloud foundry

We develop a spring-boot service, which offers a rest api (spring-webflux) and sends data via RabbitMQ (spring-rabbit). The service is deployed on cloud foundry and we use spring-boot in version 2.1.4. We added spring-boot-starter-data-redis to use redis to cache some data and we got the following error:
[io.netty.util.ResourceLeakDetector] [] LEAK: HashedWheelTimer.release() was not called before it's garbage-collected. See http://netty.io/wiki/reference-counted-objects.html for more information.
Recent access records:
Created at:
io.netty.util.HashedWheelTimer.<init>(HashedWheelTimer.java:284)
io.netty.util.HashedWheelTimer.<init>(HashedWheelTimer.java:217)
io.netty.util.HashedWheelTimer.<init>(HashedWheelTimer.java:196)
io.netty.util.HashedWheelTimer.<init>(HashedWheelTimer.java:178)
io.netty.util.HashedWheelTimer.<init>(HashedWheelTimer.java:162)
io.lettuce.core.resource.DefaultClientResources.<init>(DefaultClientResources.java:169)
io.lettuce.core.resource.DefaultClientResources$Builder.build(DefaultClientResources.java:532)
io.lettuce.core.resource.DefaultClientResources.create(DefaultClientResources.java:233)
io.lettuce.core.AbstractRedisClient.<init>(AbstractRedisClient.java:98)
io.lettuce.core.RedisClient.<init>(RedisClient.java:87)
io.lettuce.core.RedisClient.create(RedisClient.java:124)
org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory.lambda$createClient$7(LettuceConnectionFactory.java:971)
java.base/java.util.Optional.orElseGet(Unknown Source)
org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory.createClient(LettuceConnectionFactory.java:971)
org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory.afterPropertiesSet(LettuceConnectionFactory.java:273)
org.springframework.cloud.service.keyval.RedisConnectionFactoryCreator.create(RedisConnectionFactoryCreator.java:88)
org.springframework.cloud.service.keyval.RedisConnectionFactoryCreator.create(RedisConnectionFactoryCreator.java:31)
org.springframework.cloud.Cloud.getServiceConnector(Cloud.java:288)
org.springframework.cloud.Cloud.getSingletonServiceConnector(Cloud.java:202)
org.springframework.cloud.config.java.CloudServiceConnectionFactory.redisConnectionFactory(CloudServiceConnectionFactory.java:260)
org.springframework.cloud.config.java.CloudServiceConnectionFactory.redisConnectionFactory(CloudServiceConnectionFactory.java:242)
...
This error only happens when we run the service on cloud foundry, if we run it locally, we don't get any error.
We don't do any configuration of the connection factory or the stringRedisTemplate on our side and only use stringRedisTemplate, which is configured by the spring-autoconfiguration.
We use following configuration for redis on cloud foundry:
#Configuration
#Profile( "cloud" )
public class CloudSpecificConfig extends AbstractCloudConfig {
#Bean
public RedisConnectionFactory redisConnectionFactory() {
return connectionFactory().redisConnectionFactory();
}
}
And this is how we use the template
#Component
#RequiredArgsConstructor
public final class RequestUtil {
private final StringRedisTemplate myRedisTemplate;
public String cacheId(String id, String value) {
myRedisTemplate.opsForValue().set( id, value );
}
}
These are our spring dependencies:
<properties>
<spring-boot-version>2.1.4.RELEASE</spring-boot-version>
</properties>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
<version>${spring-boot-version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<version>${spring-boot-version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
<version>${spring-boot-version}</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-sleuth</artifactId>
<version>${spring-boot-version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cloud-connectors</artifactId>
<version>${spring-boot-version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
<version>${spring-boot-version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<version>${spring-boot-version}</version>
</dependency>
We are quite confused on our side, since we didn't do any specific configuration on our side. It looks for us like there is something wrong with the spring configuration on the cloud. Are we doing something wrong? Do we need to configure something differently? Is this a bug?
This is what I had to do
I will see if I can find a more elegant way
#Bean(destroyMethod = "shutdown")
public DefaultClientResources lettuceClientResources() {
return DefaultClientResources.create();
}
#SuppressWarnings("unused")
#Bean
public RedisConnectionFactory redisConnectionFactory(DefaultClientResources dependency) {
return connectionFactory().redisConnectionFactory("redis-pcf-service");
}
In the end the issue disappeared on our side, because we changed the redis client from lettuce to jedis.
We had the problem with lettuce that we would lose the connection to our redis service on our cloud infrastructure. But since there was an update to the redis service at same time as we changed the client, we don't really know if it was related to lettuce.
Maybe there also just something wrong in the auto-configuration in conjunction with the redis service on our cloud instructure, which is based on cloudfoundry

Not able to read configuration from Consul in spring-boot application

I am creating a Spring Boot application, which will read configuration like DB properties from Consul. But I am not able to read the key value from Consul using my application. Following is, what I am trying to do.
**pom.xml**
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.tuturself</groupId>
<artifactId>spring-boot-consul</artifactId>
<version>1.0-SNAPSHOT</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.4.RELEASE</version>
<relativePath/>
</parent>
<properties>
<java.version>1.8</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<spring.retry.version>1.2.1.RELEASE</spring.retry.version>
<consul.version>1.1.2.RELEASE</consul.version>
<consul.discovery.version>1.1.2.RELEASE</consul.discovery.version>
<jackson.version>2.8.1</jackson.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-consul-all</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-consul-discovery</artifactId>
<version>${consul.discovery.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.retry</groupId>
<artifactId>spring-retry</artifactId>
<version>${spring.retry.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>${jackson.version}</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>${jackson.version}</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.5.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<executions>
<execution>
<goals>
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-consul-dependencies</artifactId>
<version>1.2.1.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
</project>
And Following is my Main class:
#EnableRetry
#RefreshScope
#EnableDiscoveryClient
#SpringBootApplication
#ComponentScan("com.test.*")
public class SpringBootConsulApplication {
private static ConsulConfiguration consulConfiguration;
public static void main(String[] args) {
try {
String consulHost = System.getProperty("spring.cloud.consul.host");
System.out.println("consulHost ::" + consulHost);
String consulPort = System.getProperty("spring.cloud.consul.port");
System.out.println("consulPort ::" + consulPort);
String consulPrefix = System.getProperty("spring.cloud.consul.config.prefix");
System.out.println("consulPrefix ::" + consulPrefix);
new SpringApplicationBuilder(SpringBootConsulApplication.class).web(true).run(args);
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
And I am reading the consul properties using the #Value annotation:
#Configuration
#EnableConfigurationProperties(PropertySourceBootstrapProperties.class)
public class ConsulConfiguration {
#Value("${cassandra.host}")
private String cassandraHost;
#Value("${cassandra.user}")
private String userName;
#Value("${cassandra.password}")
private String password;
}
I have my bootstrap.yml in resources folder:
spring:
cloud:
consul:
host: localhost
port: 8500
enabled: true
config:
enabled: true
prefix: config/application
defaultContext: apps
profileSeparator: '::'
application:
name: spring-boot-consul
Consul is up and running in my local system on localhost:8500 where I have the file config/application/spring-boot-consul.yml file;
spring:
application:
name: spring-boot-consul
cassandra:
host: 127.0.0.1:9042,127.0.0.2:9042
user: my_user
password: my_pass
pooling:
maxThread: 10
timeout: 50
keyspace:
name: test_keyspace
readConsistency: ONE
writeConsistency: ONE
When I am strating the application, it is showing not able to bind cassandra.host in my ConsulConfiguration class. Thus stopping the application. Any hints , What I am doing wrong here?
You can find a working example here.
Consul Configuration KV Store
You need to store your properties in the Consul KV store either from Consul UI or from the command line. The Consul Agent will not load your properties from the file system. To load the properties from the command line, you can use the following command once the Consul Agent is up and running. The YAML data can be read from a file by prefixing the file name with the # symbol.
./consul kv put config/application/data #spring-boot-consul.yml
where config/application/data is the key name.
If the data is successfully written in the KV, you should get the following response,
Success! Data written to: config/application/data
You can also fetch the properties from the KV by using the following command,
$ ./consul kv get config/application/data
cassandra:
host: 127.0.0.1:9042,127.0.0.2:9042
user: my_user
password: my_pass
You can also view the properties from the Consul Web UI,
Changes to bootstrap.yml
You need to modify your bootstrap.yml slightly. Here are the changes:
prefix value to config
defaultContext value to application
Added format to yaml
Added data-key by the name of data to fetch the YAML blob.
spring:
profiles: default
cloud:
consul:
host: localhost
port: 8500
config:
enabled: true
prefix: config
defaultContext: application
data-key: data
profileSeparator: '::'
format: yaml
application:
name: spring-boot-consul
Changes to ConsulConfiguration
#Configuration
#RefreshScope
public class ConsulConfiguration {
#Value("${cassandra.host}")
private String cassandraHost;
#Value("${cassandra.user}")
private String userName;
#Value("${cassandra.password}")
private String password;
#PostConstruct
public void postConstruct() {
// to validate if properties are loaded
System.out.println("** cassandra.host: " + cassandraHost);
System.out.println("** cassandra.user: " + userName);
System.out.println("** cassandra.password: " + password);
}
}
Changes to Application class,
#EnableRetry
#RefreshScope
#EnableDiscoveryClient
#EnableAutoConfiguration
#EnableConfigurationProperties
#SpringBootApplication
#ComponentScan("com.test.*")
public class SpringBootConsulApplication {
public static void main(String[] args) {
...
}
}

Spring Cloud Config client not loading the value from config server

I am facing below issue while I try to run my Spring Cloud Config Client:
Caused by: java.lang.IllegalArgumentException: Could not resolve placeholder 'DATABASE_NAME' in string value "${DATABASE_NAME}"
at org.springframework.util.PropertyPlaceholderHelper.parseStringValue(PropertyPlaceholderHelper.java:174)
at org.springframework.util.PropertyPlaceholderHelper.replacePlaceholders(PropertyPlaceholderHelper.java:126)
at org.springframework.core.env.AbstractPropertyResolver.doResolvePlaceholders(AbstractPropertyResolver.java:204
My dependencies in POM.xml are as below:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.3.6.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config</artifactId>
<version>1.1.1.RELEASE</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Brixton.SR3</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
The bootstrap.yml
spring:
application:
name: my-config-client
cloud:
services:
registrationMethod: route
config:
enabled: true
uri: http://localhost:${config.port:8888}
The application.yml is as below:
# HTTP Server
server:
port: 2222
# Spring properties
spring:
profiles:
active: dev
#Disable HTTP Basic Authentication
security:
basic:
enabled: false
The class where I am trying to access the property is as below:
#RefreshScope
#Component
public class MyProperty {
#Value("${DATABASE_NAME}")
private String databaseName;
public String getDatabaseName() {
return databaseName;
}
}
My config server is running fine. When I use this url on browser http://localhost:8888/configserver/dev, It gives the below result:
{
"name":"configserver",
"profiles":[
"dev"
],
"label":"master",
"version":"c991526a93fb776e37e18e138c7485d894d6ea4f",
"propertySources":[
{
"name":"https://onestash.abc.com/scm/kapmol/microservice-config-repo.git/configserver.properties",
"source":{
"DATABASE_NAME":"ABC",
"CONVERT_USERS":"Y",
"LRDS_JNDI_NAME":"jdbc/tds_new"
}
}
]
}
I tried with all the posts who were facing this issue. But, it is not working for me. May be, I am missing some points. If anybody can provide help, it would be great.
Thanks
There are some breaking changes with the new spring cloud module read more: here.
Bootstrap, provided by spring-cloud-commons, is no longer enabled by
default. If your project requires it, it can be re-enabled by
properties or by a new starter.
To re-enable by properties set spring.cloud.bootstrap.enabled=true or spring.config.use-legacy-processing=true. These need to be set as
an environment variable, java system property or a command line
argument.
The other option is to include the new spring-cloud-starter-bootstrap
It worked for me by adding these dependencies:
<parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-parent</artifactId>
<version>2020.0.0</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring-cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-bootstrap</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-config</artifactId>
</dependency>
I referred this post answered by 'spencergibb' and able to resolve the issue. I added "spring.config.name" in my bootstrap.yml file of client application and resolved the issue. Now, my bootstrap.yml look like as below:
spring:
application:
name: my-config-client
cloud:
services:
registrationMethod: route
config:
name: configserver
enabled: true
uri: http://localhost:${config.port:8888}

Micro-services: Zuul & consul in Spring cloud application

I'm trying to create a Spring cloud microservice application using Zuul and Consul.
I have 2 components in my project:
api-gateway microservice using Zuul
Hello world microservice (a simple hello world Rest Webservice)
Here is the code of The api-gateway:
#SpringBootApplication
#EnableZuulProxy
#EnableDiscoveryClient
public class ZuulApplication {
public static void main(String[] args) {
SpringApplication.run(ZuulApplication.class, args);
}
}
The pom.xml
<parent>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-parent</artifactId>
<version>Brixton.M3</version>
</parent>
<properties>
<java.version>1.8</java.version>
<spring.cloud.consul.version>1.0.0.M4</spring.cloud.consul.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-commons</artifactId>
</dependency>
<dependency>
<!-- Setup Spring Boot -->
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<!-- Setup Spring MVC & REST, use Embedded Tomcat -->
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-config-server</artifactId>
</dependency>
<dependency>
<!-- Spring Cloud starter -->
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-zuul</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-consul-all</artifactId>
<version>${spring.cloud.consul.version}</version>
</dependency>
</dependencies>
application.yml
zuul:
routes:
hello1:
path: /hello1/**
serviceId: microservice-example
logging:
level:
org.springframework: INFO
com.netflix: DEBUG
bootstrap.yml
spring:
application:
name: edge-server
cloud:
consul:
config:
enabled: true
host: localhost
port: 8500
Here is the code of hello microservice:
#SpringBootApplication
#EnableConfigServer
#EnableDiscoveryClient
#RestController
public class Application {
#RequestMapping(value="/hello1",method = RequestMethod.GET)
public String hello() {
System.out.print("hello1");
return "Hello1";
}
public static void main(String[] args) {
new SpringApplicationBuilder(Application.class).web(true).run(args);
}
}
bootstrap.yml:
spring:
application:
name: microservice-example
profiles:
active: native
cloud:
consul:
config:
enabled: true
host: localhost
port: 8500
But, when I start the api-gateway I got the following exception:
Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.cloud.netflix.zuul.filters.RouteLocator]: Factory method 'routeLocator' threw exception; nested exception is java.lang.IllegalStateException: Unable to locate service in consul agent: edge-server
at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:189) ~[spring-beans-4.2.3.RELEASE.jar:4.2.3.RELEASE]
at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:588) ~[spring-beans-4.2.3.RELEASE.jar:4.2.3.RELEASE]
... 69 common frames omitted
Caused by: java.lang.IllegalStateException: Unable to locate service in consul agent: edge-server
at org.springframework.cloud.consul.discovery.ConsulDiscoveryClient.getLocalServiceInstance(ConsulDiscoveryClient.java:66) ~[spring-cloud-consul-discovery-1.0.0.M4.jar:1.0.0.M4]
This issue is fixed in Brixton.M3 (1.0.0.M5). As mentioned above this was an issue with spring-cloud-consul. The new version is working fine

Resources