Docker Compose + Consul + Spring Boot + FeignClient

Este art铆culo muestra un ejemplo de c贸mo elevar el entorno de desarrollo local usando Docker Compose, Consul, Make para la aplicaci贸n Spring Boot (y no solo), usando, por ejemplo, PostgreSQL y Browserless.


Plan:


  1. Configuraci贸n de servicios en Docker Compose
  2. Registro de servicios en Consul'e y adici贸n de variables al repositorio de Consul'a
  3. Crear Makefile
  4. Configuraci贸n de PostgreSQL
  5. Usando FeignClient
  6. Conclusi贸n

La aplicaci贸n es absolutamente in煤til: despu茅s de un enlace a una p谩gina, devuelve un enlace a la imagen m谩s grande de esta p谩gina. Browserless recuperar谩 la imagen, y en PostgreSQL este caso se guardar谩.


Enlace al proyecto: https://bitbucket.org/maximka777/consul-docker-spring-cloud/src/master/ .


1. Configuraci贸n de servicios en Docker Compose


Lo primero que debe hacer es crear un archivo de configuraci贸n docker-compose.yml para contenedores docker:


 touch docker-compose.yml 

Este archivo contiene la versi贸n de docker-compose:


 version: '3.4' 

Configuraci贸n de red:


 networks: lan: 

Y la configuraci贸n de los servicios necesarios, en este caso Consul, Browserless y PostgreSQL:


 services: consul: image: consul:1.1.0 hostname: localhost networks: - lan ports: - 8500:8500 postgres: image: postgres:11.0 hostname: localhost networks: - lan ports: - 5432:5432 environment: POSTGRES_PASSWORD: password POSTGRES_DB: example_app browserless: image: browserless/chrome hostname: localhost networks: - lan ports: - 3000:3000 

POSTGRES_PASSWORD : contrase帽a de la base de datos de usuario por defecto postgres , POSTGRES_DB : base de datos creada autom谩ticamente en el contenedor.


Para iniciar los servicios, necesita ejecutar el comando:


 docker-compose up 

Estamos esperando el final de cargar im谩genes de contenedores y lanzar contenedores. Para detener la ejecuci贸n de contenedores, use el docker-compose down . Despu茅s de iniciar todos los contenedores, puede ir a la direcci贸n en el navegador localhost:8500 : el cliente web Consul deber铆a abrirse (Fig. 1).


Figura 1


Figura 1


2. Registro de servicios en Consul'e y agregar variables al almacenamiento de Consul'a


Puede registrar servicios en Consul enviando varias solicitudes posteriores a la direcci贸n de localhost:8500/v1/agent/service/register , por ejemplo, usando curl.


Ponga todas las llamadas curl en el script bash.


 #!/bin/bash curl -s -XPUT -d"{ \"Name\": \"postgres\", \"ID\": \"postgres\", \"Tags\": [ \"postgres\" ], \"Address\": \"localhost\", \"Port\": 5432, \"Check\": { \"Name\": \"PostgreSQL TCP on port 5432\", \"ID\": \"postgres\", \"Interval\": \"10s\", \"TCP\": \"postgres:5432\", \"Timeout\": \"1s\", \"Status\": \"passing\" } }" localhost:8500/v1/agent/service/register curl -s -XPUT -d"{ \"Name\": \"browserless\", \"ID\": \"browserless\", \"Tags\": [ \"browserless\" ], \"Address\": \"localhost\", \"Port\": 3000, \"Check\": { \"Name\": \"Browserless TCP on port 3000\", \"ID\": \"browserless\", \"Interval\": \"10s\", \"TCP\": \"browserless:3000\", \"Timeout\": \"1s\", \"Status\": \"passing\" } }" localhost:8500/v1/agent/service/register curl -s -XPUT -d"{ \"Name\": \"example.app\", \"ID\": \"example.app\", \"Tags\": [ \"example.app\" ], \"Address\": \"localhost\", \"Port\": 8080, \"Check\": { \"Name\": \"example.app HTTP on port 8080\", \"ID\": \"example.app\", \"Interval\": \"10s\", \"HTTP\": \"example.app:8080/actuator/health\", \"Timeout\": \"1s\", \"Status\": \"passing\" } }" localhost:8500/v1/agent/service/register 

chmod +x register-services.sh - para hacer que el archivo sea ejecutable.


Despu茅s de ejecutar el script, nuestro PostgreSQSL y Browserless aparecer谩n en la lista de servicios registrados en Consule'e (Fig. 2).


Figura 2


Figura 2


La figura muestra que la comprobaci贸n de PostgreSQL falla con un error (no afectar谩 la esencia) .


Agregue la configuraci贸n al almacenamiento de clave / valor de Consul. Cree la variable test.property en el directorio example.app :


 curl --request PUT --data TEST \ localhost:8500/v1/kv/example.app/test.property 

Si hay muchas variables, es mejor usar un script bash.


3. Crear un Makefile


Para simplificar el lanzamiento de todo esto, escriba Makefile`:


 docker_up: @docker-compose up -d consul_up: @./register-services.sh && \ ./register-variables.sh compile: @cd example.app && mvn package run: @cd example.app && java -jar target/example.app-1.0-SNAPSHOT.jar up: docker_up consul_up compile run down: @docker-compose down 

Advertencia: 隆 Makefile utiliza un tipo especial de sangr铆a!


El comando de make up iniciar谩 todo el entorno.


4. Configuraci贸n de PostgreSQL


A continuaci贸n, se gener贸 un proyecto Spring Boot b谩sico (Maven) utilizando el inicializador de la aplicaci贸n Spring Boot https://start.spring.io/ .


Se han agregado las siguientes dependencias a pom.xml :


  <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <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.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-consul-config</artifactId> <version>2.0.1.RELEASE</version> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-consul-discovery</artifactId> <version>2.0.1.RELEASE</version> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> <version>2.0.2.RELEASE</version> </dependency> <dependency> <groupId>org.postgresql</groupId> <artifactId>postgresql</artifactId> <scope>runtime</scope> </dependency> 

Por los nombres de los paquetes est谩 claro por qu茅 son necesarios.
Escribamos la configuraci贸n para el DataSource. En el archivo bootstrap.properties , suelte las configuraciones:


 spring.cloud.consul.host=localhost spring.cloud.consul.port=8500 spring.cloud.consul.config.enabled=true spring.cloud.consul.config.prefix= spring.cloud.consul.config.defaultContext=example.app spring.cloud.consul.discovery.register=false spring.cloud.service-registry.auto-registration.enabled=false spring.jpa.properties.hibernate.temp.use_jdbc_metadata_defaults = false spring.jpa.database-platform=org.hibernate.dialect.PostgreSQL9Dialect spring.jpa.show-sql=false spring.jpa.hibernate.ddl-auto=create 

En application.yml :


 example.app: db: name: 'example_app' feign: client: config: default: connectTimeout: 20000 readTimeout: 20000 loggerLevel: basic management: endpoint: health: show-details: always endpoints: web.exposure: include: '*' 

Y la clase de configuraci贸n en s铆:


 @Configuration public class PersistenceConfiguration { @Value("${example.app.db.name}") private String databaseName; @Autowired private DiscoveryClient discoveryClient; @Bean @Primary public DataSource dataSource() { var postgresInstance = getPostgresInstance(); return DataSourceBuilder .create() .username("postgres") .password("password") .url(format("jdbc:postgresql://%s:%s/%s", postgresInstance.getHost(), postgresInstance.getPort(), databaseName)) .driverClassName("org.postgresql.Driver") .build(); } private ServiceInstance getPostgresInstance() { return discoveryClient.getInstances("postgres") .stream() .findFirst() .orElseThrow(() -> new IllegalStateException("Unable to discover a Postgres instance")); } } 

El m茅todo getPostgresInstance() toma la primera instancia de servicio con la etiqueta postgres registrada en Consul. El m茅todo dataSource() es el bean de DataSource.


A continuaci贸n, declaramos un repositorio con operaciones b谩sicas en la entidad Image , que almacena la direcci贸n de la p谩gina y la direcci贸n de la imagen:


 @Repository public interface ImageRepository extends JpaRepository<Image, Long> { } 

5. Usando FeignClient


A continuaci贸n, colocaremos un script JS en los recursos, que extraer谩 la imagen m谩s grande de la p谩gina.


 module.exports = async ({page, context}) => { const {url} = context; await page.goto(url); await page.evaluate(_ => { window.scrollBy(0, window.innerHeight); }); const data = await page._client.send('Page.getResourceTree') .then(tree => { return Array.from(tree.frameTree.resources) .filter(resource => resource.type === 'Image' && resource.url && resource.url.indexOf('.svg') == -1) .sort((a, b) => b.contentSize - a.contentSize)[0]; }); return { data, type: 'json' }; }; 

Defina la interfaz de BlowserlessClient:


 @FeignClient("browserless") //   browserless      public interface BrowserlessClient { @PostMapping("/function") ImageInfo findLargestImage(LargestImageRequest request); //       Browserless'     class ImageInfo { private String url; public String getUrl() { return url; } } //  ,    Browserless  ,    class LargestImageRequest { private String code; private BrowserlessContext context; public LargestImageRequest(String code, BrowserlessContext context) { this.code = code; this.context = context; } public String getCode() { return code; } public BrowserlessContext getContext() { return context; } } //        class BrowserlessContext { private String url; public BrowserlessContext(String url) { this.url = url; } public String getUrl() { return url; } } } 

M茅todo de servicio solicitando una imagen y almacenando en la base de datos:


 public Image findLargestImage(String url) { var browserlessContext = new BrowserlessContext(url); var largestImageRequest = new LargestImageRequest(getLargestImageScript, browserlessContext); var imageInfo = browserlessClient.findLargestImage(largestImageRequest); var image = new Image(); image.setSourceUrl(url); image.setImageUrl(imageInfo.getUrl()); return imageRepository.save(image); } 

Controlador de funcionalidad:


 public class MainController { private static Logger log = LoggerFactory.getLogger(MainController.class); @Autowired private ImageService imageService; @Value("${test.property}") private String testProperty; @GetMapping("/largest-image") public ResponseEntity<Image> getTitle(@RequestParam("url") String url) { return ResponseEntity.ok(imageService.findLargestImage(url)); } @GetMapping("/property") public ResponseEntity<String> getProperty() { return ResponseEntity.ok(testProperty); } } 

Aqu铆 el campo testProperty extrae del almac茅n de claves / valores de testProperty .


6. El fin


Eso es todo!


Espero haber podido mostrar una posible configuraci贸n de las herramientas presentadas y este art铆culo ser谩 煤til para alguien.


No hay muchas explicaciones en este art铆culo, ya que creo que en este caso es m谩s f谩cil entender el c贸digo.


Enlaces utiles:
1) https://docs.docker.com/compose/overview/ - Documentaci贸n de Docker Compose
2) https://www.consul.io/intro/index.html - introducci贸n al c贸nsul
3) http://matt.might.net/articles/intro-to-make/ - introducci贸n para hacer
4) https://cloud.spring.io/spring-cloud-netflix/multi/multi_spring-cloud-feign.html - documentaci贸n sobre Feign

Source: https://habr.com/ru/post/es429472/


All Articles