0

I already went through many links like @RefreshScope and /refresh not working and Spring Boot 2: Refresh properties on the fly not working, but still things are not working for me.

I've developed spring-cloud-config-server

when I hit

POST ==> http://localhost:8002/actuator/refresh

I get the response

[ "config.client.version" ]

It looks like its somehow not loading the changed properties

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 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.4.2</version>
        <relativePath /> <!-- lookup parent from repository -->
    </parent>
    <groupId>com.example</groupId>
    <artifactId>user-service</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>user-service</name>
    <description>Demo project for Spring Boot</description>
    <properties>
        <java.version>1.8</java.version>
        <spring-cloud.version>2020.0.2</spring-cloud.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-config</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-bootstrap</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-circuitbreaker-resilience4j</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>
    <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>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
                <configuration>
                    <excludes>
                        <exclude>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </exclude>
                    </excludes>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

PropertyAccessBean.java:

@Component
@ConfigurationProperties(prefix="property-file")
public class PropertyAccessBean {

    private String name;
    private String description;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }
}

PropertyAccessValue.java:

public class PropertyAccessValue {

    private String name;
    private String description;


    public PropertyAccessValue(String name, String description) {
        this.name = name;
        this.description = description;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    @Override
    public String toString() {
        return "PropertyAccessValue{" +
                "name='" + name + '\'' +
                ", description='" + description + '\'' +
                '}';
    }
}

UserController.java:

@RestController
@RequestMapping("/users")
@RefreshScope
@Slf4j
public class UserController {

    @Autowired
    private UserService userService;

    @PostMapping("/")
    public User saveUser(@RequestBody User user) {
        log.info("Inside saveUser of UserController");
        return userService.saveUser(user);
    }

    @GetMapping("/{id}")
    public ResponseTemplateVO getUserWithDepartment(@PathVariable("id") Long userId) {
        log.info("Inside getUserWithDepartment of UserController");
//      return userService.getUserWithDepartment(userId);
        
//      return userService.getUserWithDepartmentWithFeign(userId);
        
        return userService.getUserWithDepartmentWithFeignAndResilient4J(userId);
    }
    
    
    @Autowired
    PropertyAccessBean propertyAccessBean;

    @GetMapping("/readkey")
    public PropertyAccessValue accesPropertyFile(){

        return new PropertyAccessValue(propertyAccessBean.getName(),
                propertyAccessBean.getDescription());
    }
}

bootstrap.properties:

spring:
  cloud:
    config:
      enabled: true
      uri: http://localhost:9296
      

application.yml:

server:
  port: 8002

spring:
  application:
    name: user-service 

resilience4j:
  circuitbreaker:
    instances:
      department:
        register-health-indicator: true
        ring-buffer-size-in-closed-state: 5
        ring-buffer-size-in-half-open-state: 3
        wait-duration-in-open-state: 10s
        failure-rate-threshold: 50
        record-exceptions:
        - org.springframework.web.client.HttpServerErrorException
        - org.springframework.web.client.ResourceAccessException
        - java.util.concurrent.TimeoutException
        - java.io.IOException
        

management:
  endpoints:
    web:
      exposure:
        include: refresh
  endpoint:
    health:
      show-details: always
      

           
Michael Piefel
  • 18,660
  • 9
  • 81
  • 112
PAA
  • 1
  • 46
  • 174
  • 282
  • Why is `bootstrap.properties` in YAML format? – Michael Piefel May 04 '21 at 16:57
  • Whats the correct way? People often use the bootstrap.yml file – PAA May 04 '21 at 17:16
  • 1
    I would say that if a file _contains_ YAML, it should be called `*.yml`. When I try to parse `bootstrap.properties` (which is, despite its name, a YAML file) as a properties file, I get 5 properties, three of them empty. – Michael Piefel May 05 '21 at 07:28

3 Answers3

0

The mistake lies in your bootstrap.properties file to really be YAML instead of a properties file. Please refer to the documentation for properties file format. Your file is equivalent to the following:

spring=
cloud=
config=
enabled=true
uri=http://localhost:9296

So this file does not set sping.cloud.config.enabled to true, but instead some property called enabled, which is probably unused.

Michael Piefel
  • 18,660
  • 9
  • 81
  • 112
0

I was facing the same issue, where after making a POST request to the actuator refresh endpoint, I was getting:

[ "config.client.version" ]

This shows it noticed the changed but unable to fetch the changed data.

I got the refresh endpoint working, after adding spring.cloud.config.name in the bootstrap file of the service that was reading the data from the config server.

See below my bootstrap.yml for my product-service API.

spring:
  config:
    import: optional:configserver:http://localhost:8080/
  cloud:
    config:
      name: product-service
Sy3d
  • 197
  • 2
  • 2
  • 18
-1

After 10 hours of crazy debugging I had to replace yml to properties file and its started working.

I'm not sure why yml was unable to pick up the changes, surely its a bug!.

application.properties

spring.cloud.config.enabled=true
spring.cloud.config.uri=http://localhost:9296

management.endpoints.web.exposure.include=*
management.endpoint.health.enabled=true

server.port=8002

spring.application.name=user-service

resilience4j.circuitbreaker.instances.department.register-health-indicator=true
resilience4j.circuitbreaker.instances.department.ring-buffer-size-in-closed-state=5
resilience4j.circuitbreaker.instances.department.ring-buffer-size-in-half-open-state=3
resilience4j.circuitbreaker.instances.department.wait-duration-in-open-state=10s
resilience4j.circuitbreaker.instances.department.failure-rate-threshold=50
resilience4j.circuitbreaker.instances.department.record-exceptions=org.springframework.web.client.HttpServerErrorException

Here is the response

[
    "property-file.name",
    "config.client.version",
    "property-file.description"
]
PAA
  • 1
  • 46
  • 174
  • 282