1. Introducción

En el siguiente post veremos un nuevo servidor de Spring Cloud, el Servidor de Configuración!. En la entrada Spring Boot – Property analizamos como inyectar las propiedades definidas en el fichero application.yml, que se debía encontrar en la raiz de nuestro classpath, en nuestros beans de Spring.

Sin embargo esta solución no es la más mantenible ya que un cambio de properties obligaba a realizar un build y un despliegue.

Para solucionar este problema la gente de Netflix OSS ha creado el Server Configuration!. Se encargará de obtener los ficheros de propiedades de un repositorio git para proveerlo a los distintos microservicios de nuestro sistema.

Os podéis descargar el código de ejemplo de mi GitHub aquí.

Tecnologías empleadas:

  • Java 8
  • Gradle 3.1
  • SpringBoot 1.5.2.RELEASE
  • SpringCloud 1.1.5.RELEASE
  • Spring 4.3.7.RELEASE

2. Eureka server

Necesitaremos Eureka para poder realizar el registro y descubrimiento de servicios. Recordar que simplemente basta con utilizar la anotación @EnableEurekaServer.

group 'com.jorgehernandezramirez.spring.springboot'
version '1.0-SNAPSHOT'

buildscript {
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath("org.springframework.boot:spring-boot-gradle-plugin:1.5.2.RELEASE")
    }
}

apply plugin: 'java'
apply plugin: 'idea'
apply plugin: 'maven'
apply plugin: 'spring-boot'

dependencyManagement {
    imports {
        mavenBom 'org.springframework.cloud:spring-cloud-dependencies:Camden.SR6'
    }
}

sourceCompatibility = 1.8

springBoot {
    mainClass = "com.jorgehernandezramirez.spring.springcloud.eureka.Application"
}

repositories {
    mavenCentral()
}

dependencies {
    compile 'org.springframework.cloud:spring-cloud-starter-eureka-server'
}
server:
   port: 8761
package com.jorgehernandezramirez.spring.springcloud.eureka;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;

@EnableEurekaServer
@SpringBootApplication
public class Application {

    public Application(){
        //For Spring
    }

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

3. Configuration Server

Añadimos la dependencia org.springframework.cloud:spring-cloud-config-server

group 'com.jorgehernandezramirez.spring.springboot'
version '1.0-SNAPSHOT'

buildscript {
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath("org.springframework.boot:spring-boot-gradle-plugin:1.5.2.RELEASE")
    }
}

apply plugin: 'java'
apply plugin: 'idea'
apply plugin: 'maven'
apply plugin: 'spring-boot'

dependencyManagement {
    imports {
        mavenBom 'org.springframework.cloud:spring-cloud-dependencies:Camden.SR6'
    }
}

sourceCompatibility = 1.8

springBoot {
    mainClass = "com.jorgehernandezramirez.spring.springcloud.eureka.Application"
}

repositories {
    mavenCentral()
}

dependencies {
    compile 'org.springframework.cloud:spring-cloud-config-server'
    compile 'org.springframework.cloud:spring-cloud-starter-eureka'   
}

Utilizamos la anotación @EnableConfigServer para arrancar nuestro servidor de configuración. Como queremos que se registre en Eureka añadimos la anotación @EnableDiscoveryClient

package com.jorgehernandezramirez.spring.springcloud.eureka;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.config.server.EnableConfigServer;

@EnableConfigServer
@EnableDiscoveryClient
@SpringBootApplication
public class Application {

    public Application(){
        //For Spring
    }

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

Añadimos la configuración para que obtenga los properties de un repositorio de git. Esto se define en el fichero bootstrap.yml. Por otra parte el nombre con el que nuestro microservicio se registrará en eureka será configuration

spring:
  application:
    name: configuration
  cloud:
    config:
      server:
        git:
          uri: https://github.com/JorgeHernandezRamirez/ConfigurationFiles

En el repositorio ConfigurationFiles de nuestro git definimos los siguientes ficheros

variable: valor-desarrollo
variable: valor

Por otro lado en application.yml definimos el puerto en el que queremos arrancar así como la conexión con eureka.

server:
   port: 8889

eureka:
  client:
    serviceUrl:
      defaultZone: http://localhost:8761/eureka/
  instance:
    hostname: localhost
    nonSecurePort: 8889

management:
  security:
     enabled: false

4. Backend

Nos creamos un microservicio de prueba que llamaremos backend y que deberá obtener las propiedades del servidor de configuración. Para ello tenenemos que:

  • Configurar conexión con Eureka
  • Configurar conexión con el Server Configuration
  • Exponer controlador de test que nos devuelva la propiedad variable definida en los ficheros de propiedades de nuestro repo de git

Utilizamos las dependencias spring-cloud-starter-eureka y spring-cloud-starter-config para realizar la conexión con Eureka y con el Configuration Server

group 'com.jorgehernandezramirez.spring.springboot'
version '1.0-SNAPSHOT'

buildscript {
    repositories {
        mavenCentral()
    }
    dependencies {
        classpath("org.springframework.boot:spring-boot-gradle-plugin:1.5.2.RELEASE")
    }
}

apply plugin: 'java'
apply plugin: 'idea'
apply plugin: 'maven'
apply plugin: 'spring-boot'

dependencyManagement {
    imports {
        mavenBom 'org.springframework.cloud:spring-cloud-dependencies:Camden.SR2'
    }
}

sourceCompatibility = 1.8

springBoot {
    mainClass = "com.jorgehernandezramirez.spring.springcloud.backend.Application"
}

repositories {
    mavenCentral()
}

dependencies {
    compile 'org.springframework.cloud:spring-cloud-starter-eureka'
    compile 'org.springframework.cloud:spring-cloud-starter-config'
}

Arrancamos SpringBoot a través del siguiente main.

package com.jorgehernandezramirez.spring.springcloud.backend;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;

@SpringBootApplication
@EnableDiscoveryClient
public class Application {

    public Application(){
        //For Spring
    }

    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

Configuramos la conexión con el servidor de configuración. El descubrimiento lo haremos a través de Eureka indicando que queremos obtener el host y puerto del microservicio cuyo nombre es CONFIGURATION.

spring:
  application:
     name: backend
  cloud:
     config:
       discovery:
         service-id: CONFIGURATION
         enabled: true
       fail-fast: true

Configuramos la conexión con eureka

server:
   port: 8080

eureka:
  client:
    serviceUrl:
      defaultZone: http://localhost:8761/eureka/
  instance:
    hostname: localhost
    nonSecurePort: 8080

Creamos un controlador de pruebas donde inyectar la propiedad variable que nos proporcionará el servidor de Configuración.

package com.jorgehernandezramirez.spring.springcloud.backend.controller;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/test")
public class TestController {

    @Value("${variable}")
    private String variableFromConfigServer;

    public TestController(){
        //For Spring
    }

    @RequestMapping(method = RequestMethod.GET)
    public String getVariableFromConfigServer() {
        return variableFromConfigServer;
    }
}

Llegado a este punto puedes estar preguntándote por qué para el microservicio de backend hemos llegado a definir hasta 3 ficheros de propiedades. El backend-dev.yml y backend.yml que se encuentran en nuestro repo de Git y el application.yml que se encuentra en la raiz de nuestro classpath. La respuesta es que las propiedades se obtienen de todos ellos!. A continuación se muestran los ficheros de propiedades que se consideran así como su preferencia por orden.

  • backend-[PERFIL].yml (REPO GIT)
  • application-[PEFIL].yml (REPO GIT)
  • backend.yml (REPO GIT)
  • application.yml (REPO GIT)
  • application-[PERFIL].yml (CLASSPATH)
  • application.yml (CLASSPATH)

PD:backend es el nombre del microservicio que se está considerando. Recordar que se define en el fichero bootstrap.yml que se debe encontrar en el classpath de la aplicación

5. Testeando nuestra aplicación

Compilamos los 3 módulos

gradle build

Arrancamos los 3 microservicios

java -jar eureka-1.0-SNAPSHOT.jar
java -jar configuration-1.0-SNAPSHOT.jar
java -jar backend-1.0-SNAPSHOT.jar

Atacamos al controlador /test

curl http://localhost:8080/test 

Resultado

valor

Arrancando backend con el perfil dev

java -jar backend-1.0-SNAPSHOT.jar --spring.profiles.active=dev

Atacamos al controlador /test

curl http://localhost:8080/test 

Resultado

valor-desarrollo