Docker Compose + Consul + Spring Boot + FeignClient

本文显示了一个示例,该示例说明了如何使用Docker Compose,Consul,Make for the Spring Boot应用程序(不仅限于),例如使用PostgreSQL和Browserless来提升本地开发环境。


计划:


  1. 在Docker Compose中配置服务
  2. 在Consul'e中注册服务并将变量添加到Consul'a存储库中
  3. 创建Makefile
  4. PostgreSQL配置
  5. 使用FeignClient
  6. 结论

该应用程序绝对没有用:在链接到页面之后,它返回到该页面上最大图像的链接。 该图像将由Browserless检索,并且在PostgreSQL中将保存这种情况。


链接到项目: https : //bitbucket.org/maximka777/consul-docker-spring-cloud/src/master/


1.在Docker Compose中设置服务


首先要做的是docker-compose.yml容器创建docker-compose.yml配置文件:


 touch docker-compose.yml 

该文件包含docker-compose的版本:


 version: '3.4' 

网络配置:


 networks: lan: 

以及必要服务的配置,在本例中为Consul,Browserless和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默认情况下用户数据库的密码postgresPOSTGRES_DB在容器中自动创建的数据库。


要启动服务,您需要运行以下命令:


 docker-compose up 

我们正在等待加载容器映像和启动容器的结束。 要停止容器运行,请使用docker-compose down 。 启动所有容器后,您可以转到localhost:8500浏览器中的地址localhost:8500 -Consul Web客户端应打开(图1)。


图1


图1


2.在Consul'e中注册服务并将变量添加到Consul'a的存储中


您可以在Consul中注册服务,方法是将几个后期请求发送到localhost:8500/v1/agent/service/register地址localhost:8500/v1/agent/service/register ,例如,使用curl。


将所有curl调用放入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 -services.sh-使文件可运行。


执行脚本后,我们的PostgreSQSL和Browserless将出现在Consule'e中已注册服务的列表中(图2)。


图2


图2


该图显示PostgreSQL检查失败并显示错误- (这不会影响本质)


将配置添加到领事的键/值存储中。 在example.app目录中创建变量test.property


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

如果变量很多,最好使用bash脚本。


3.创建一个Makefile


为了简化所有这些的启动,编写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 

警告: Makefile使用特殊类型的缩进!


make up命令将启动整个环境。


4. PostgreSQL配置


接下来,使用Spring Boot应用程序初始化程序https://start.spring.io/生成了一个基本的Spring Boot项目(Maven)。


以下依赖项已添加到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> 

从软件包名称中可以很清楚地知道为什么需要它们。
让我们为DataSource编写配置。 在bootstrap.properties文件中,删除配置:


 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 

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: '*' 

和配置类本身:


 @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")); } } 

getPostgresInstance()方法采用在Consul中注册了postgres标签的第一个服务实例。 dataSource()方法是DataSource的bean。


接下来,我们声明一个在Image实体上具有基本操作的存储库,该存储库存储页面地址和图像地址:


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

5.使用FeignClient


接下来,我们将JS脚本放入资源中,这将从页面中拉出最大的图像。


 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' }; }; 

定义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; } } } 

请求图像并存储在数据库中的服务方法:


 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); } 

功能控制器:


 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); } } 

这里的testProperty字段testPropertytestProperty的键/值存储区testProperty提取的。


6.结束


仅此而已!


我希望我能够展示所介绍工具的可能配置,并且本文对某人有用。


本文中没有太多解释,因为我认为在这种情况下更容易理解代码。


有用的链接:
1) https://docs.docker.com/compose/overview/-Docker Compose文档
2) https://www.consul.io/intro/index.html-领事简介
3) http://matt.might.net/articles/intro-to-make/-制作简介
4) https://cloud.spring.io/spring-cloud-netflix/multi/multi_spring-cloud-feign.html-Feign的文档

Source: https://habr.com/ru/post/zh-CN429472/


All Articles