Skip to content

labcabrera/sample-spring-consul

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

13 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Ejemplo de integración de Consul y Spring Boot

Build Status

Introducción

En este ejemplo se muestran las capacidades de Spring Cloud Consul tanto para el descubrimiento de servicios como para el uso de una configuración distribuída. En este ejemplo utilizaremos una instancia local de Consul a través de la imagen de DockerHub.

Nuestra aplicación será muy sencilla, simplemente utilizaremos las anotaciones

@Configuration
@EnableAutoConfiguration
@EnableDiscoveryClient
public class ConsulSampleApplication {

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

}

Docker

En primer lugar nos descargaremos la última versión de consul y levantaremos el contenedor con el siguiente comando:

docker run  -p 8500:8500 -p 8600:8600/udp --name=consul consul:latest agent -server -bootstrap -ui -client=0.0.0.0

Una vez arrancado podremos entrar en la consola web: http://localhost:8500/ui

También podremos comprobar el estado haciendo:

curl http://localhost:8500/v1/health/service/consul?pretty

En este caso necesitaremos conocer la IP con la que el contenedor resolverá el servicio que queremos descubrir. Para ello simplemente ejecutaremos el comando

$ docker network inspect bridge
[
    {
        "Name": "bridge",
        "Id": "31de39115c56a7129ad297ff038fbd08ff9bf731f035f53c24f29c9578f89c04",
        "Created": "2018-05-17T08:38:32.147242687+02:00",
        "Scope": "local",
        "Driver": "bridge",
        "EnableIPv6": false,
        "IPAM": {
            "Driver": "default",
            "Options": null,
            "Config": [
                {
                    "Subnet": "172.17.0.0/16",
                    "Gateway": "172.17.0.1"
                }
            ]
        },
        "Internal": false,
        "Attachable": false,
        "Ingress": false,
        "ConfigFrom": {
            "Network": ""
        },
        "ConfigOnly": false,
        "Containers": {
            "18478727e1166e5ead3e5d3f14a3dec50f22fe64a59be683184f7b89d862dd5b": {
                "Name": "consul",
                "EndpointID": "732cd731fb163c1f86e1457e5524ef1f7ce52eab96a79d0b458975dc97128c27",
                "MacAddress": "02:42:ac:11:00:02",
                "IPv4Address": "172.17.0.2/16",
                "IPv6Address": ""
            }
        },
        "Options": {
            "com.docker.network.bridge.default_bridge": "true",
            "com.docker.network.bridge.enable_icc": "true",
            "com.docker.network.bridge.enable_ip_masquerade": "true",
            "com.docker.network.bridge.host_binding_ipv4": "0.0.0.0",
            "com.docker.network.bridge.name": "docker0",
            "com.docker.network.driver.mtu": "1500"
        },
        "Labels": {}
    }
]

Y vemos que resuelve nuestra IP como 172.17.0.1.

Este será el valor en nuestro ejemplo que tendremos que indicar en la configuración de service-discovery:

spring:
  cloud:
    consul:
      discovery:
        enabled: true
        register: true
        prefer-ip-address: true
        ip-address: 172.17.0.1
        instanceId: ${spring.application.name}:${random.value}

Podemos comprobar que resuelve correctamente nuestra ip ejecutando:

docker exec -it ${containerId} ping 172.17.0.1 -c 1

Probando la configuración

Para consultar la configuración vamos a incluir un servicio REST sencillo que simplemente devolverá los valores recuperados:

@EnableAutoConfiguration
@EnableDiscoveryClient
@SpringBootApplication
public class ConsulSampleApplication {

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

}

Después desde la consola podremos de forma sencilla crear las siguientes claves de configuración:

Adding custom configuration property

Donde el formato de la clave será /config/${appName}/keyValue.

Para comprobar que nuestra aplicación resuelve correctamente ese valor podemos hacer la siguiente petición:

curl http://localhost:8080/api/v1/config/foo

Al tener las dependencias de Spring Actuator también podremos inspeccionar nuestra configuración accediendo a http://localhost:8080/env, donde veremos la sección de configuración consul:config/consul-sample/.

Que devolverá el valor que hemos introducido anteriormente.

Vemos que si desde la consola de administración cambiamos el valor nuestra aplicación automáticamente actualiza dicho valor:

2018-05-17 11:20:23.252  INFO 27664 --- [ask-scheduler-3] s.c.a.AnnotationConfigApplicationContext : Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@1176c553: startup date [Thu May 17 11:20:23 CEST 2018]; root of context hierarchy
2018-05-17 11:20:23.283  INFO 27664 --- [ask-scheduler-3] f.a.AutowiredAnnotationBeanPostProcessor : JSR-330 'javax.inject.Inject' annotation found and supported for autowiring
2018-05-17 11:20:23.295  INFO 27664 --- [ask-scheduler-3] trationDelegate$BeanPostProcessorChecker : Bean 'configurationPropertiesRebinderAutoConfiguration' of type [org.springframework.cloud.autoconfigure.ConfigurationPropertiesRebinderAutoConfiguration$$EnhancerBySpringCGLIB$$e02d6bc] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)
2018-05-17 11:20:23.507  INFO 27664 --- [ask-scheduler-3] b.c.PropertySourceBootstrapConfiguration : Located property source: CompositePropertySource [name='consul', propertySources=[ConsulPropertySource {name='config/consul-sample/'}, ConsulPropertySource {name='config/application/'}]]
2018-05-17 11:20:23.509  INFO 27664 --- [ask-scheduler-3] o.s.boot.SpringApplication               : No active profile set, falling back to default profiles: default
2018-05-17 11:20:23.511  INFO 27664 --- [ask-scheduler-3] s.c.a.AnnotationConfigApplicationContext : Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@720b5947: startup date [Thu May 17 11:20:23 CEST 2018]; parent: org.springframework.context.annotation.AnnotationConfigApplicationContext@1176c553
2018-05-17 11:20:23.517  INFO 27664 --- [ask-scheduler-3] f.a.AutowiredAnnotationBeanPostProcessor : JSR-330 'javax.inject.Inject' annotation found and supported for autowiring
2018-05-17 11:20:23.538  INFO 27664 --- [ask-scheduler-3] o.s.boot.SpringApplication               : Started application in 0.5 seconds (JVM running for 131.782)
2018-05-17 11:20:23.539  INFO 27664 --- [ask-scheduler-3] s.c.a.AnnotationConfigApplicationContext : Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@720b5947: startup date [Thu May 17 11:20:23 CEST 2018]; parent: org.springframework.context.annotation.AnnotationConfigApplicationContext@1176c553
2018-05-17 11:20:23.540  INFO 27664 --- [ask-scheduler-3] s.c.a.AnnotationConfigApplicationContext : Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@1176c553: startup date [Thu May 17 11:20:23 CEST 2018]; root of context hierarchy
2018-05-17 11:20:23.734  INFO 27664 --- [ask-scheduler-3] o.s.c.e.event.RefreshEventListener       : Refresh keys changed: [foo]

También podremos consultar el valor directamente desde la API REST de Consul a través de la siguiente petición:

$ curl http://localhost:8500/v1/kv/config/consul-sample/foo
[{"LockIndex":0,"Key":"config/consul-sample/foo","Flags":0,"Value":"Zm9vVmFsdWVVcGRhdGVk","CreateIndex":120,"ModifyIndex":142}]l

Donde obtendremos el valor codificado en Base64.

Service discovery

Para utilizar el sistema de registro de servicios primero declararemos el siguiente bean:

@LoadBalanced
@Bean
public RestTemplate loadbalancedRestTemplate() {
  return new RestTemplate();
}

Después simplemente crearemos un cliente que obtenga el endpoint desde Consul:

@RestController
public class ApiConsumerController {

  @Value("http://${spring.application.name}/api/v1/dummy")
  private String endpoint;

  @Autowired
  private RestTemplate restTemplate;

  @GetMapping("/api/v1/consumer")
  public DummyMessage consume() {
    return restTemplate.getForObject(endpoint, DummyMessage.class);
  }

}

Siendo consul-sample nuestro identificador de servicio con el que nos hemos registrado en Consul (este valor será el spring.application.name).

Para probar que funciona simplemente haremos la siguiente petición

$ curl http://localhost:8080/api/v1/consumer
{"id":"a66a3e5d-4679-4c6d-8d5b-1d284807a81f","subject":"Hello","body":"Generated at 2018-05-17T12:18:47.566"}

Inspeccionando el log veremos:

2018-05-17 12:18:46.647  INFO 3849 --- [nio-8080-exec-7] s.c.a.AnnotationConfigApplicationContext : Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@5c666bb7: startup date [Thu May 17 12:18:46 CEST 2018]; parent: org.springframework.boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext@859ea42
2018-05-17 12:18:46.724  INFO 3849 --- [nio-8080-exec-7] f.a.AutowiredAnnotationBeanPostProcessor : JSR-330 'javax.inject.Inject' annotation found and supported for autowiring
2018-05-17 12:18:47.110  INFO 3849 --- [nio-8080-exec-7] c.netflix.config.ChainedDynamicProperty  : Flipping property: consul-sample.ribbon.ActiveConnectionsLimit to use NEXT property: niws.loadbalancer.availabilityFilteringRule.activeConnectionsLimit = 2147483647
2018-05-17 12:18:47.191  INFO 3849 --- [nio-8080-exec-7] c.n.u.concurrent.ShutdownEnabledTimer    : Shutdown hook installed for: NFLoadBalancer-PingTimer-consul-sample
2018-05-17 12:18:47.280  INFO 3849 --- [nio-8080-exec-7] c.netflix.loadbalancer.BaseLoadBalancer  : Client: consul-sample instantiated a LoadBalancer: DynamicServerListLoadBalancer:{NFLoadBalancer:name=consul-sample,current list of Servers=[],Load balancer stats=Zone stats: {},Server stats: []}ServerList:null
2018-05-17 12:18:47.292  INFO 3849 --- [nio-8080-exec-7] c.n.l.DynamicServerListLoadBalancer      : Using serverListUpdater PollingServerListUpdater
2018-05-17 12:18:47.354  INFO 3849 --- [nio-8080-exec-7] c.netflix.config.ChainedDynamicProperty  : Flipping property: consul-sample.ribbon.ActiveConnectionsLimit to use NEXT property: niws.loadbalancer.availabilityFilteringRule.activeConnectionsLimit = 2147483647
2018-05-17 12:18:47.363  INFO 3849 --- [nio-8080-exec-7] c.n.l.DynamicServerListLoadBalancer      : DynamicServerListLoadBalancer for client consul-sample initialized: DynamicServerListLoadBalancer:{NFLoadBalancer:name=consul-sample,current list of Servers=[172.17.0.1:8080, 172.17.0.1:8080, 172.17.0.1:8080],Load balancer stats=Zone stats: {unknown=[Zone:unknown;  Instance count:3;  Active connections count: 0;  Circuit breaker tripped count: 0;  Active connections per server: 0.0;]
},Server stats: [[Server:172.17.0.1:8080;  Zone:UNKNOWN;  Total Requests:0;  Successive connection failure:0;  Total blackout seconds:0;  Last connection made:Thu Jan 01 01:00:00 CET 1970;  First connection made: Thu Jan 01 01:00:00 CET 1970;  Active Connections:0;  total failure count in last (1000) msecs:0;  average resp time:0.0;  90 percentile resp time:0.0;  95 percentile resp time:0.0;  min resp time:0.0;  max resp time:0.0;  stddev resp time:0.0]
]}ServerList:ConsulServerList{serviceId='consul-sample', tag=null}
2018-05-17 12:18:48.315  INFO 3849 --- [erListUpdater-0] c.netflix.config.ChainedDynamicProperty  : Flipping property: consul-sample.ribbon.ActiveConnectionsLimit to use NEXT property: niws.loadbalancer.availabilityFilteringRule.activeConnectionsLimit = 2147483647

Y eso es todo por el momento.

About

Spring Cloud Consul app sample

Topics

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages