Docker Compose + Consul + Spring Boot + FeignClient

تُظهر هذه المقالة مثالًا على كيفية رفع بيئة التطوير المحلية باستخدام تطبيق Docker Compose و Consul و Make for the Spring Boot (وليس فقط) ، باستخدام ، على سبيل المثال ، PostgreSQL و Browserless.


الخطة:


  1. تكوين الخدمات في Docker Compose
  2. تسجيل الخدمات في القنصل وإضافة المتغيرات إلى مستودع القنصل
  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:


 touch docker-compose.yml 

يحتوي هذا الملف على إصدار إنشاء عامل الميناء:


 version: '3.4' 

تكوين الشبكة:


 networks: lan: 

وتكوين الخدمات اللازمة ، في هذه الحالة القنصل و 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 - كلمة مرور لقاعدة بيانات المستخدم بشكل افتراضي postgres ، POSTGRES_DB - إنشاء قاعدة بيانات تلقائيًا في الحاوية.


لبدء الخدمات تحتاج إلى تشغيل الأمر:


 docker-compose up 

نحن في انتظار نهاية تحميل صور الحاويات وإطلاق الحاويات. لإيقاف تشغيل الحاويات ، استخدم الأمر docker-compose down . بعد بدء تشغيل جميع الحاويات ، يمكنك الانتقال إلى العنوان في متصفح localhost:8500 - يجب فتح عميل الويب القنصل (الشكل 1).


الشكل 1


الشكل 1


2. تسجيل الخدمات في القنصل وإضافة المتغيرات لتخزين القنصل


يمكنك تسجيل الخدمات في القنصل عن طريق إرسال العديد من الطلبات اللاحقة إلى عنوان localhost:8500/v1/agent/service/register ، على سبيل المثال ، باستخدام curl.


ضع كل مكالمات الضفيرة في سكربت باش.


 #!/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 - لجعل الملف قابلاً للتشغيل.


بعد تنفيذ البرنامج النصي ، ستظهر PostgreSQSL و Browserless في قائمة الخدمات المسجلة في Consule'e (الشكل 2).


الشكل 2


الشكل 2


يوضح الشكل أن اختبار PostgreSQL فشل مع وجود خطأ - (لن يؤثر على الجوهر) .


إضافة التكوين إلى تخزين المفتاح / القيمة القنصل. أنشئ متغير test.property في دليل example.app :


 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 الأساسي (Maven) باستخدام مُهيئ تطبيق Spring Boot https://start.spring.io/ .


تمت إضافة التبعيات التالية إلى 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> 

من أسماء الحزم يتضح سبب الحاجة إليها.
دعنا نكتب تكوين مصدر البيانات. في ملف 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() مثيل الخدمة الأول مع علامة postgres المسجلة في القنصل. أسلوب dataSource() هو فول مصدر البيانات.


بعد ذلك ، نعلن عن مستودع يحتوي على العمليات الأساسية على كيان 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 سحب حقل testProperty من مفتاح / قيمة 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/ar429472/


All Articles