En arquitecturas distribuidas, con múltiples microservicios desplegados en distintos servidores, realizar cambios de configuración en cada una de ellos puede tornarse una tarea compleja y demandante.
Spring Cloud Config es una herramienta que permite exteriorizar y centralizar la configuración de los microservicios en un solo lugar, lo que facilita enormemente la administración de la configuración del sistema.
Por default, Spring Cloud Config utiliza Git como repositorio para centralizar la configuración de cada uno de los servicios. Esto nos permite aprovechar algunas sus ventajas:
- Control de cambios
- Utilizar labels para hacer referencia a versiones específicas
Setup del proyecto y sus dependencias
Código fuente: https://github.com/ignacioerro/spring-cloud-config-sample
- config-server: Spring Cloud Config Server
- consumer-service y producer-service: Spring Cloud Config Clients
NOTA: Para generar los proyectos voy a utilizar Spring Initializer
Config server
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-config-server</artifactId> </dependency>
ConfigServerApplicationcon
la anotación @EnableConfigServer
:@EnableConfigServer @SpringBootApplication public class ConfigServerApplication { public static void main(String
[] args) { SpringApplication.run(ConfigServerApplication.class, args); } }
config-server/src/main/resources/application.yml
server: port: ${PORT:8888} spring: application: name: config-server cloud: config: server: git: uri: https://github.com/ignacioerro/spring-cloud-configuration
El config-server expone un conjunto de recursos para que los servicios puedan acceder a sus configuraciones:
/{application}/{profile}[/{label}]
/{application}-{profile}.yml
/{label}/{application}-{profile}.yml
application.yml
: propiedades comunes a todos los servicios que componen el sistema.
global-message: Configuration server is alive!
consumer-service.yml
: propiedades exclusivas para el consumer-service.
server: port: ${PORT:8889} message: Consumer configuration server is alive!
producer-service.yml
: propiedades exclusivas para el producer-service.
server: port: ${PORT:8890} message: Producer configuration server is alive!
Config clients: microservices
spring-cloud-starter-config
y spring-boot-starter-web
:<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>
2. Algo muy importante que debemos tener en cuenta, es que debemos renombrar el archivo de propiedades application.[properties|yml] por bootstrap.[properties|yml] en las 2 aplicaciones que acabamos de crear. Y en cada uno agregamos el nombre de la aplicación, el cual es necesario para mapear la aplicación con el archivo de configuración correspondiente.
consumer-service/src/main/resources/bootstrap.yml
spring: application: name: consumer-service
producer-service/src/main/resources/bootstrap.yml
spring: application: name: producer-service
message
: es una propiedad específica, configurada en cada servicio.global-message
: es una propiedad global del sistema, que puede ser accedida por ambos servicios.
consumer-service/src/main/java/com/example/consumerservice/ConsumerServiceApplication.java
@RestController class Consumer { @Value("${message}") private String message; @Value("${global-message}") private String globalMessage; @RequestMapping(method = RequestMethod.GET) public Map<String, String> message() { Map<String, String> response = new HashMap<>(); response.put("message", message); response.put("global-message", globalMessage); return response; } }
producer-service/src/main/java/com/example/producerservice/ProducerServiceApplication.java
@RestController class Producer { @Value("${message}") private String message; @Value("${global-message}") private String globalMessage; @RequestMapping(method = RequestMethod.GET) public Map<String, String> message() { Map<String, String> response = new HashMap<>(); response.put("message", message); response.put("global-message", globalMessage); return response; } }
Ejecución de los servicios
config-server
ya que cada uno se levantó en el puerto especificado: 8889 el consumer-service
y 8890 el producer-service
.config-server
en el puerto 8888, podemos ver las propiedades específicas y globales que están configuradas para cada aplicación:Configuración de consumer-service
: http://localhost:8888/consumer-service/master
{ "name": "consumer-service", "profiles": ["master"], "label": null, "version": null, "state": null, "propertySources": [{ "name": "https://github.com/ignacioerro/spring-cloud-configuration/consumer-service.yml", "source": { "server.port": "${PORT:8889}", "message": "Consumer configuration server is alive!" } }, { "name": "https://github.com/ignacioerro/spring-cloud-configuration/application.yml", "source": { "global-message": "Configuration server is alive!" } }] }
Configuración de producer-service
: http://localhost:8888/producer-service/master
{ "name": "producer-service", "profiles": ["master"], "label": null, "version": null, "state": null, "propertySources": [{ "name": "https://github.com/ignacioerro/spring-cloud-configuration/producer-service.yml", "source": { "server.port": "${PORT:8890}", "message": "Producer configuration server is alive!" } }, { "name": "https://github.com/ignacioerro/spring-cloud-configuration/application.yml", "source": { "global-message": "Configuration server is alive!" } }] }
(message)
, y un mensaje global a todo el sistema (global-message)
:Aplicación consumer-service
: http://localhost:8889
{ "global-message": "Configuration server is alive!", "message": "Consumer configuration server is alive!" }
Aplicación producer-service
: http://localhost:8890
{ "global-message": "Configuration server is alive!", "message": "Producer configuration server is alive!" }
Más claro imposible. Muchas gracias