Este artigo mostra um exemplo de como elevar o ambiente de desenvolvimento local usando o Docker Compose, Consul, Make para o aplicativo Spring Boot (e não apenas), usando, por exemplo, PostgreSQL e Browserless.
Plano:
- Configurando serviços no Docker Compose
- Registro de serviços no Consul'e e adição de variáveis ao repositório do Consul'a
- Criar Makefile
- Configuração do PostgreSQL
- Usando FeignClient
- Conclusão
O aplicativo é absolutamente inútil: após um link para uma página, ele retorna um link para a maior imagem desta página. A imagem será recuperada pelo Browserless, e no PostgreSQL este caso será salvo.
Link para o projeto: https://bitbucket.org/maximka777/consul-docker-spring-cloud/src/master/ .
1. Configurando serviços no Docker Compose
A primeira coisa a fazer é criar um arquivo de configuração docker-compose.yml
para contêineres do docker:
touch docker-compose.yml
Este arquivo contém a versão do docker-compose:
version: '3.4'
Configuração de rede:
networks: lan:
E a configuração dos serviços necessários, neste caso Consul, Browserless e 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
- senha para o banco de dados do usuário por padrão postgres
, POSTGRES_DB
- banco de dados criado automaticamente no contêiner.
Para iniciar os serviços, você precisa executar o comando:
docker-compose up
Estamos aguardando o fim do carregamento de imagens de contêineres e do lançamento de contêineres. Para parar a execução de contêineres, use o docker-compose down
. Após iniciar todos os contêineres, você pode ir para o endereço no navegador localhost:8500
- o cliente Web Consul deve abrir (Fig. 1).

Figura 1
2. Registro de serviços no Consul'e e adição de variáveis ao armazenamento do Consul'a
Você pode registrar serviços no Consul enviando várias solicitações posteriores ao endereço do localhost:8500/v1/agent/service/register
, por exemplo, usando curl.
Coloque todas as chamadas curl no script bash.
chmod +x register-services.sh
- para tornar o arquivo executável.
Após a execução do script, nosso PostgreSQSL e o Browserless aparecerão na lista de serviços registrados no Consule'e (Fig. 2).

Figura 2
A figura mostra que a verificação do PostgreSQL falha com um erro - (isso não afeta a essência) .
Adicione a configuração ao armazenamento de chave / valor do Consul. Crie a variável test.property
no diretório example.app
:
curl --request PUT --data TEST \ localhost:8500/v1/kv/example.app/test.property
Se houver muitas variáveis, é melhor usar um script bash.
3. Criando um Makefile
Para simplificar o lançamento de tudo isso, escreva 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
Aviso: Makefile
usa um tipo especial de recuo!
O comando de make up
iniciará todo o ambiente.
4. Configuração do PostgreSQL
Em seguida, um projeto básico do Spring Boot (Maven) foi gerado usando o inicializador do aplicativo Spring Boot https://start.spring.io/ .
As seguintes dependências foram adicionadas ao 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>
A partir dos nomes dos pacotes, fica claro por que eles são necessários.
Vamos escrever a configuração para o DataSource. No arquivo bootstrap.properties
, descarte as configurações:
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
Em 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: '*'
E a própria classe de configuração:
@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")); } }
O método getPostgresInstance()
obtém a primeira instância de serviço com a tag postgres
registrada no Consul. O método dataSource()
é o bean do DataSource.
Em seguida, declaramos um repositório com operações básicas na entidade Image
, que armazena o endereço da página e o endereço da imagem:
@Repository public interface ImageRepository extends JpaRepository<Image, Long> { }
5. Usando FeignClient
Em seguida, soltaremos um script JS nos recursos, que extrairão a maior imagem da 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 a interface BlowserlessClient:
@FeignClient("browserless")
Método de serviço que solicita uma imagem e armazena no banco de dados:
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 funcionalidade:
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); } }
Aqui, o campo testProperty
obtido do armazenamento de chave / valor do testProperty
.
6. O fim
Isso é tudo!
Espero ter conseguido mostrar uma possível configuração das ferramentas apresentadas e este artigo será útil para alguém.
Não há muitas explicações neste artigo, pois acredito que, neste caso, é mais fácil entender o código.
Links úteis:
1) https://docs.docker.com/compose/overview/ - documentação do Docker Compose
2) https://www.consul.io/intro/index.html - introdução ao Consul
3) http://matt.might.net/articles/intro-to-make/ - introdução ao make
4) https://cloud.spring.io/spring-cloud-netflix/multi/multi_spring-cloud-feign.html - documentação sobre Feign