1. Introducción
Hola a todos y bienvenidos de nuevo post de nuestro blog!. En esta ocasión hablaremos de Consul como sustituto de Eureka y del servidor de configuración. Y es que Consul nos permitirá, entre otras cosas, llevar a cabo el registro y descubrimiento de servicios. Además tiene la capacidad de almacenar pares clave/valor que podrán ser recuperados desde nuestras aplicaciones. Este post es análogo al que publicamos hace unos meses sobre Eureka + Ribbon + Feign, pero en lugar de utilizar Eureka utilizando Consul. Así que si no te lo has leído te invito a que le eches un vistazo antes de empezar con este.
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
- Spring 4.3.7.RELEASE
- SpringCloud 1.1.5.RELEASE
- Consul 0.9.2
2. Consul
Como ya hemos comentado Consul es un tecnología que nos permite llevar a cabo el registro y el descubrimiento de nuestros servicios así como la persistencia y recuperación de las propiedades de nuestra aplicación. Utilizaremos docker para desplegar una instancia de Consul como viene siendo habitual en nuestro blog.
Se muestra el fichero docker-compose.yml que nos permitirá correr un contenedor docker de Consul
Para arrancar nuestro contenedor basta con
Accedemos a http://localhost:8500 y podemos ver la administración de Consul!
Propiedades Key/Value
Una de las características interesantes de Consul es su capacidad para persistir y devolver propiedades comportándose como servidor de configuración. Para dar de alta dichas propiedades basta con acceder a la sección KEY/VALUE y empezar a crear nuestras propiedades.
3. Ficheros de configuración
Hacemos uso de las dependencias spring-cloud-starter-consul-all
y spring-cloud-starter-feign
dentro de nuestro build.gradle
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.Application" } repositories { mavenCentral() } dependencies { compile 'org.springframework.cloud:spring-cloud-starter-consul-all' compile 'org.springframework.cloud:spring-cloud-starter-feign' }
En el fichero application.yml establecemos el host y puerto de Consul. Además establecemos cómo queremos que nuestro microservicio se registre. Es importante destacar que hay que indicarle a Consul a través de la propiedad spring.cloud.consul.discovery.healthCheckPath
la url de chequeo del estado de la aplicación.
server: port: 8080 spring: cloud: consul: host: localhost port: 8500 discovery: port: 8080 prefer-ip-address: true healthCheckPath: /health healthCheckInterval: 5s
En el fichero bootstrap.yml indicamos el nombre de la aplicación y el formato de propiedades que esperamos que Consul nos devuelva. En nuestro caso indicamos el formato YAML.
Por defecto SpringBoot irá a buscar las propiedades en las siguientes rutas (se muestran por prioridad)
- /config/[NOMBRE_MICROSERVICIO],[PERFIL]
- /config/[NOMBRE_MICROSERVICIO]
- /config/application,[PERFIL]
- /config/application
Sin embargo, si lo que queremos es definir un yaml dentro de Consul en lugar de tener que dar de alta una a una las propiedades de nuestra aplicación deberemos definir dentro de bootstrap.yml la propiedad spring.clolud.consul.config.format
con el valor YAML tal y como se ha mostrado anteriormente. En este caso dichos yaml se deben crear en alguna de las siguientes rutas dentro de consul (se muestran por prioridad)
- /config/[NOMBRE_MICROSERVICIO],[PERFIL]/data
- /config/[NOMBRE_MICROSERVICIO]/data
- /config/application,[PERFIL]/data
- /config/application/data
En nuestro caso definiremos nuestro YAML dentro de la ruta /config/application/data
4. Ficheros Java
Se muestra el main de la aplicación.
package com.jorgehernandezramirez.spring.springcloud; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; import org.springframework.cloud.netflix.feign.EnableFeignClients; @SpringBootApplication @EnableDiscoveryClient @EnableFeignClients("com.jorgehernandezramirez.spring.springcloud.feign") public class Application { public Application(){ //For Spring } public static void main(String[] args) { SpringApplication.run(Application.class, args); } }
Para hacer uso de Ribbon y Feign es necesario definir dentro del contexto de Spring un objeto de la clase RestTemplate haciendo uso de la anotación @LoadBalanced
para indicar que se va a hacer uso del descubrimiento de servicios.
package com.jorgehernandezramirez.spring.springcloud.configuration; import org.springframework.cloud.client.loadbalancer.LoadBalanced; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.client.RestTemplate; @Configuration public class RestTemplateConfiguration { @Bean @LoadBalanced RestTemplate loadBalancedRestTemplate() { return new RestTemplate(); } }
Mostramos la clase TestFeign que nos permitirá invocar a nuestras Apis a través de Feign.
package com.jorgehernandezramirez.spring.springcloud.feign; import org.springframework.cloud.netflix.feign.FeignClient; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; @FeignClient("springcloudwebtest") public interface TestFeign { @RequestMapping(value="/ping", method = RequestMethod.GET) String doAlive(); }
Mostramos la clase de test que permitirá invocar a las Apis haciendo uso de Ribbon y Feign apoyándose en Consul
package com.jorgehernandezramirez.spring.springcloud.controller; import com.jorgehernandezramirez.spring.springcloud.feign.TestFeign; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import org.springframework.web.client.RestTemplate; @RestController @RequestMapping("/ping") public class TestController { @Autowired private RestTemplate restTemplate; @Autowired private TestFeign testFeign; public TestController(){ //For Spring } @RequestMapping public String doAlive() { return "Alive!"; } @RequestMapping("/rest") public String doRestAlive() { return new RestTemplate().getForObject("http://localhost:8080/ping", String.class); } @RequestMapping("/rest/ribbon") public String doRestAliveUsingEurekaAndRibbon() { return restTemplate.getForObject("http://springcloudwebtest/ping", String.class); } @RequestMapping("/rest/feign") public String doRestAliveUsingFeign() { return testFeign.doAlive(); } }
Se muestra la clase PropertyController en donde se inyectará la propiedad property, que debe estar definida dentro de Consul, para exponer su valor a través de un controlador.
package com.jorgehernandezramirez.spring.springcloud.controller; import org.springframework.beans.factory.annotation.Value; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/property") public class PropertyController { @Value("${property}") private String property; public PropertyController(){ //For Spring } @RequestMapping public String getProperty() { return property; } }
5. Probando la aplicación
A continuación compilamos nuestros fuentes y arrancamos.
Compilamos
Arrancamos la aplicación
Para probar que desde RestTemplate se está haciendo uso del descubrimiento de servicios utilizando Consul, atacamos a la url http://localhost:8080/rest/ping/ribbon obteniendo
Alive!
Por otro lado para probar que Feign está haciendo uso del descubrimiento de servicios utilizando Consul, atacamos a la url http://localhost:8080/rest/ping/feign obteniendo
Alive!
Por último comprobamos que la propiedad definida dentro de Consul se ha inyectado correctamente. Para ello atacamos a la url http://localhost:8080/property obteniendo
property value