مرحبا بالجميع!
في هذه المقالة ، سوف نوضح المكونات الأساسية لإنشاء خدمات مايكروية RESTful باستخدام سجل خدمات القنصل ، Boot Spring لجميع السقالات ، حقن التبعية ، Maven للتجميع ، بالإضافة إلى Spring REST و Java RESTful Jersey / JaxRS API.
المزايا الرئيسية للخدمات الصغيرة:
- تساعد Microservices على فك شفرتك
- تسمح الخدمات المصغرة للفرق المختلفة بالعمل على مكونات صغيرة باستخدام تقنيات مستقلة ، مما يوفر نشرًا أكثر أمانًا وتكرارًا. يدعم Spring Boot عمليات التنفيذ المختلفة لإنشاء واجهة برمجة تطبيقات REST
- لا يعتمد الاكتشاف ومكالمة الخدمات على النظام الأساسي للخدمة
- تقوم Swagger بإنشاء وثائق API قوية وواجهة اتصال
إذا كنت لا تستخدم بالفعل الخدمات المصغرة ، فأنت لم تنجح في الدخول في مرحلة المتابعين الأوائل على منحنى تصور التكنولوجيا ، وربما يكون هذا هو الوقت المناسب للبدء.

على مدار العقدين الماضيين ، أصبحت المؤسسة مرنة للغاية في عملية SDLC لدينا ، لكن تطبيقاتنا ، كقاعدة عامة ، لا تزال متجانسة ، مع برطمانات ضخمة تدعم جميع واجهات برمجة التطبيقات والإصدارات المختلفة في السوق. ولكن في الوقت الحاضر ، هناك رغبة في المزيد من عمليات Lean و DevOps ، وأصبحت الوظيفة "بدون خادم". يمكن أن تقلل إعادة بناء الخدمات الصغيرة من تشابك الشفرة والموارد ، وتجعل التجميعات أصغر ، والإصدارات أكثر أمانًا ، وواجهة برمجة التطبيقات أكثر استقرارًا.
في هذه المقالة ، سنقوم بإنشاء تطبيق بسيط لإدارة محفظة سوق الأسهم يمكن للعملاء الاتصال به لتقييم محفظة الأسهم الخاصة بهم (مؤشرات الأسهم والقيم). ستقوم خدمة microservice باسترداد محفظة العميل ، وإرسالها إلى خدمة microservice للتسعير لتطبيق أحدث الأسعار ، ثم إعادة المحفظة ذات القيمة الكاملة والمجموع الفرعي ، مع توضيح كل ذلك من خلال مكالمة بقية.

قبل أن نبدأ العمل على إنشاء خدماتنا الصغيرة ، فلنعد بيئتنا من خلال إعداد القنصل.
تحميل القنصل
سوف نستخدم Hashicorp Consul لاكتشاف الخدمات ، لذلك انتقل إلى
www.consul.io/downloads.html وقم بتنزيل Consul لأنظمة Windows و Linux و Mac وما إلى ذلك. سيوفر لك هذا ملفًا قابلاً للتنفيذ تحتاج إلى إضافته إلى مسارك.
إطلاق القنصل
في موجه الأوامر ، يتم تشغيل القنصل في وضع dev:
consul agent -dev
للتحقق من أنه يعمل ، انتقل إلى المستعرض الخاص بك والوصول إلى واجهة القنصل
http: // localhost: 8500 . إذا سارت الأمور على ما يرام ، يجب على القنصل الإفادة بأنه على قيد الحياة وبصحة جيدة. بالضغط على خدمة القنصل (على اليسار) ، ستتلقى معلومات إضافية (على اليمين).

إذا كانت هناك أي مشاكل في الوقت الحالي ، فتأكد من إضافة القنصل إلى مسار التنفيذ ، ومنفذين 8500 و 8600 متاحان.
إنشاء تطبيق SpringBoot
سنستخدم
Spring Initializr ، الذي تم دمجه في معظم IDEs ، لسقالة تطبيقات SpringBoot. تستخدم لقطات الشاشة أدناه IntelliJ IDEA.
حدد "ملف / مشروع جديد" لفتح قالب مشروع جديد ، ثم "Spring Initializr".

بشكل عام ، يمكنك إعداد السقالات بدون IDE عن طريق ملء نموذج عبر الإنترنت عبر صفحة الويب
SpringBoot Initializr
start.spring.io ، والتي
ستنشئ ملفًا مضغوطًا لمشروعك الفارغ ، جاهزًا للتنزيل.
انقر على "التالي" واملأ البيانات الوصفية للمشروع. استخدم التكوين التالي:

انقر فوق "التالي" لتحديد التبعيات ، وأدخل "جيرسي" و "القنصل ديسكفري" في البحث عن التبعيات. أضف هذه التبعيات:

انقر فوق "التالي" للإشارة إلى اسم المشروع وموقعه. احتفظ بالاسم الافتراضي "محفظة" وحدد الموقع المفضل للمشروع ، ثم انقر على "إنهاء" لإنشاء المشروع وفتحه:

يمكننا استخدام application.properties الذي تم إنشاؤه ، لكن SpringBoot يتعرف أيضًا على تنسيق YAML ، وهو أسهل قليلاً في التصور ، لذا دعنا نعيد تسميته إلى application.yml.
نحن نسمي الخدمة الصغيرة "خدمة الحافظة". يمكننا تحديد منفذ أو استخدام منفذ 0 بحيث يستخدم التطبيق منفذًا متاحًا. في حالتنا ، سنستخدم 57116. إذا استضفت هذه الخدمة كحاوية Docker ، فيمكنك تعيينها إلى أي منفذ تحدده. قم بتسمية التطبيق وحدد منفذنا بإضافة ما يلي إلى application.yml الخاص بنا:
spring: application: name: portfolio-service server: port: 57116
لإتاحة خدمتنا ، أضف تعليقًا توضيحيًا إلى فئة تطبيق SpringBoot. افتح تطبيق PortfolioApplication وأضفEnableDiscoveryClient أعلى تعريف الفئة.
قم بتأكيد عمليات الاستيراد. يجب أن يبدو الفصل كما يلي:
package com.restms.demo.portfolio; import org.springframework.cloud.client.discovery.EnableDiscoveryClient; . . . @SpringBootApplication @EnableDiscoveryClient public class PortfolioApplication { public static void main(String[] args) { SpringApplication.run(PortfolioApplication.class, args); } }
(لتوضيح كيف يمكن أن تتكون الخدمات الصغيرة من منصات مستقلة ، سنستخدم جيرسي لهذه الخدمة و Spring REST للخدمة التالية).
لتهيئة خدمة الويب RESTful على جيرسي ، نحتاج إلى تحديد فئة التكوين ResourceConfig. أضف فئة JerseyConfig (للتوضيح ، سنقوم بحفظها في نفس الحزمة مثل فئة التطبيق لدينا). يجب أن يبدو مثل هذا ، بالإضافة إلى الحزمة والاستيراد الصحيحين:
@Configuration @ApplicationPath("portfolios") public class JerseyConfig extends ResourceConfig { public JerseyConfig() { register(PortfolioImpl.class); } }
لاحظ أنه يرث من ResourceConfig لتعيينه على أنه فئة تكوين جيرسي. تحدد السمةApplicationPath ("folios ") سياق المكالمة ، مما يعني أنه يجب أن تبدأ المكالمات بعنصر مسار "folios". (إذا حذفته ، فإن السياق الافتراضي هو "/").
ستعمل فئة PortfolioImpl على طلبين: تُظهر محافظ / العملاء / {customer-id} جميع المحافظ والمحافظ / العملاء / {customer-id} / Wallet / {Wallet-id} تُرجع محفظة واحدة. تتكون المحفظة من مجموعة من المؤشرات وعدد الأسهم التي يحتفظ بها هذا المؤشر. (للتوضيح ، هناك ثلاثة عملاء بمعرفات 0 و 1 و 2 ، ولكل منها ثلاث حقائب بمعرفات 0 و 1 و 2).
سيطلب منك IDE إنشاء PortfolioImpl ؛ افعل ذلك الآن. للتوضيح ، أضفه إلى نفس الحزمة. أدخل الرمز أدناه وأكد جميع عمليات الاستيراد:
@Component @Path("/") public class PortfolioImpl implements InitializingBean { private Object[][][][] clientPortfolios; @GET @Path("customer/{customer-id}") @Produces(MediaType.APPLICATION_JSON) // a portfolio consists of an array of arrays, each containing an array of // stock ticker and associated shares public Object[][][] getPortfolios(@PathParam("customer-id") int customerId) { return clientPortfolios[customerId]; } @GET @Path("customer/{customer-id}/portfolio/{portfolio-id}") @Produces(MediaType.APPLICATION_JSON) public Object[][] getPortfolio(@PathParam("customer-id") int customerId, @PathParam("portfolio-id") int portfolioId) { return getPortfolios(customerId)[portfolioId]; } @Override public void afterPropertiesSet() throws Exception { Object[][][][] clientPortfolios = { { // 3 customers, 3 portfolios each {new Object[]{"JPM", 10201}, new Object[]{"GE", 20400}, new Object[]{"UTX", 38892}}, {new Object[]{"KO", 12449}, new Object[]{"JPM", 23454}, new Object[]{"MRK", 45344}}, {new Object[]{"WMT", 39583}, new Object[]{"DIS", 95867}, new Object[]{"TRV", 384756}}, }, { {new Object[]{"GE", 38475}, new Object[]{"MCD", 12395}, new Object[]{"IBM", 91234}}, {new Object[]{"VZ", 22342}, new Object[]{"AXP", 385432}, new Object[]{"UTX", 23432}}, {new Object[]{"IBM", 18343}, new Object[]{"DIS", 45673}, new Object[]{"AAPL", 23456}}, }, { {new Object[]{"AXP", 34543}, new Object[]{"TRV", 55322}, new Object[]{"NKE", 45642}}, {new Object[]{"CVX", 44332}, new Object[]{"JPM", 12453}, new Object[]{"JNJ", 45433}}, {new Object[]{"MRK", 32346}, new Object[]{"UTX", 46532}, new Object[]{"TRV", 45663}}, } }; this.clientPortfolios = clientPortfolios; } }
يعيّن التعليق التوضيحي
للمكون هذا على أنه فئة مكون الربيع ويوفره كنقطة نهاية. تعلن التعليقات التوضيحية
للمسار حول إعلان الفصل عن أنه يتم الوصول إلى الفصل الدراسي عبر عنصر المسار "/" ، وأن مكالمتين مدعومتين متاحتين عبر المحافظ / الزبون / {العميل-معرف} والمحافظ / الزبون / {العميل-معرف} / محفظة / {محفظة- معرف} ، كما نرى من التعليقات التوضيحية للطريقة. لاحظ أن المسار ("/") هو المسار الافتراضي ، لكننا نتركه كمرجع. يتم تعيين الأساليب على أنها HTTP GET عبرGETannotation. تم تصميم طريقتنا لإرجاع مصفوفة وتعليق عليها لإرجاع Json ، بحيث تقوم بإرجاع صفيف Json. لاحظ كيفية استخدام التعليقات التوضيحية
Path Param في توقيع الطريقة لاستخراج المعلمات المعروضة من الاستعلامات المعروضة.
(بالنسبة إلى العرض التوضيحي الخاص بنا ، فإننا نعيد القيم ذات الترميز الثابت. بالطبع ، عمليًا ، سوف يستعلم التنفيذ عن قاعدة البيانات أو خدمة أو مصدر بيانات آخر بدلاً من الرمز الثابت.)
الآن قم بإنشاء مشروع وتشغيله. إذا كنت تستخدم IntelliJ ، فسيقوم بإنشاء ملف افتراضي قابل للتنفيذ ، ما عليك سوى النقر فوق السهم الأخضر "تشغيل". يمكنك أيضًا استخدام
mvn spring-boot:run
أو يمكنك تنفيذ تثبيت مخضرم وتشغيل التطبيق باستخدام java -jar ، مشيراً إلى المرطبان الذي تم إنشاؤه في الدليل الهدف:
java -jar target\portfolio-0.0.1-SNAPSHOT.jar
الآن يجب أن نرى هذه الخدمة في القنصل ، لذلك دعونا نعود إلى متصفحنا ، قم بتنزيل
http: // localhost: 8500 / ui / # / dc1 / services (أو قم بالتحديث إذا كنت موجودًا بالفعل).

حسنًا ، نرى خدمتنا هناك ، ولكن يتم عرضها على أنها فاشلة. وذلك لأن القنصل يتوقع إشارة نبض "صحية" من خدمتنا.
لتوليد إشارات نبضات القلب ، يمكننا إضافة الاعتماد على خدمة
Spring Actuator إلى دليل تطبيقنا.
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency>
أثناء وجودنا في بوم ، لاحظ أن هناك تعارضًا في الإصدار مع جيرسي بين كاتب القنصل ومبتدئ جيرسي. لتنعيم هذا ، عيّن كاتب جيرسي كأول إدمان.
يجب أن يحتوي بوم الخاص بك الآن على التبعيات التالية:
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jersey</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-consul-discovery</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies>
من خلال إعادة تشغيل القنصل ، تعرض خدمة Portfolio واحدة سعيدة:

الآن في خدمة المحفظة هناك عقدتان للإرسال: إحداها هي تنفيذ خدمة المحفظة ، والأخرى هي ضربات القلب.
دعونا نتحقق من المنفذ الذي تم تعيينه. يمكنك أن ترى ذلك في إخراج التطبيق:
INFO 19792 --- [ main] sbcetTomcatEmbeddedServletContainer : Tomcat started on port(s): 57116 (http)
يمكنك أيضًا رؤية المنفذ مباشرة في واجهة مستخدم القنصل. انقر على "خدمة العملاء" ، ثم حدد رابط "الخدمة" ارتباط التحقق من خدمة العملاء "، والذي يعرض منفذ الخدمة ، في هذه الحالة 57116.

اطلب
http: // localhost: 57116 /folios / customer / 1 / Wallet / 2 وسترى مجموعة json [["IBM"، 18343]، ["DIS"، 45673]، ["AAPL"، 23456]]
خدمتنا الصغيرة الأولى مفتوحة للأعمال!
خدمة التسعير
بعد ذلك ، سننشئ خدمة التسعير لدينا ، هذه المرة باستخدام Spring RestController بدلاً من جيرسي.
ستقبل خدمة التسعير معرف العميل ومعرف المحفظة كمعلمات وستستخدم RestTemplate لطلب خدمات المحفظة وتلقي المؤشرات والأسهم وإعادة الأسعار الحالية. (لست بحاجة إلى أن أخبرك أن هذه القيم هي أخبار مزيفة ، لذلك لا تستخدمها لاتخاذ قرارات التداول!)
قم بإنشاء مشروع جديد باستخدام المعلومات التالية:

هذه المرة ، حدد تبعيات الويب و Consul Discovery و Actuator:

اترك الاسم الافتراضي لمشروع "التسعير" وأنشئ مشروعًا في الدليل الذي تختاره.
هذه المرة سوف نستخدم application.properties بدلاً من application.yml.
قم بتعيين الاسم والمنفذ في application.properties كـ:
spring.application.name=pricing server.port=57216
ضع تعليقات توضيحية حول التسعير معEnableDiscoveryClient. يجب أن تبدو الفئة على هذا النحو ، بالإضافة إلى الحزمة والاستيراد.
@SpringBootApplication @EnableDiscoveryClient public class PricingApplication { public static void main(String[] args) { SpringApplication.run(PricingApplication.class, args); } }
ثم سننشئ فئة PricingEndpoint. هنا سأقدم مثالاً أكثر تفصيلاً ، لأنه يوضح العديد من الوظائف المهمة ، بما في ذلك اكتشاف الخدمة (البحث عن خدمة المحفظة) واستخدام RestTemplate للاستعلام:
@RestController @RequestMapping("/pricing") public class PricingEndpoint implements InitializingBean { @Autowired DiscoveryClient client; Map<String, Double> pricingMap = new HashMap<>(); RestTemplate restTemplate = new RestTemplate(); @GetMapping("/customer/{customer-id}/portfolio/{portfolio-id}") public List<String> getPricedPortfolio( @PathVariable("customer-id") Integer customerId, @PathVariable("portfolio-id") Integer portfolioId) { List<ServiceInstance> instances = client.getInstances("portfolio-service"); ServiceInstance instance = instances.stream() .findFirst() .orElseThrow(() -> new RuntimeException("not found")); String url = String.format("%s/portfolios/customer/%d/portfolio/%d", instance.getUri(), customerId, portfolioId); // query for the portfolios, returned as an array of List // of size 2, containing a ticker and a position (
للعثور على خدمة المحفظة ، نحتاج إلى الوصول إلى DiscoveryClient. من السهل الحصول على التعليقات التوضيحية التلقائية لـ Spring @.
@Autowired DiscoveryClient client;
يتم بعد ذلك استخدام مثيل DiscoveryClient للبحث عن الخدمة في المكالمة:
List<ServiceInstance> instances = client.getInstances("portfolio-service"); ServiceInstance instance = instances.stream().findFirst().orElseThrow(() -> new RuntimeException("not found"));
بعد العثور على الخدمة ، يمكننا استخدامها لتلبية طلبنا ، الذي نقوم بإنشائه وفقًا لمكالمة واجهة برمجة التطبيقات التي تم إنشاؤها في خدمة المحفظة لدينا.
String url = String.format("%s/portfolios/customer/%d/portfolio/%d", instance.getUri(), customerId, portfolioId);
أخيرًا ، نستخدم RestTemplate لتنفيذ طلب GET.
Object[] portfolio = restTemplate.getForObject(url, Object[].class);
لاحظ أنه بالنسبة إلى أجهزة التحكم في الراحة (وكذلك بالنسبة إلى وحدة تحكم طلب MVC في الربيع) ، يتم استرداد متغيرات المسار باستخدام التعليق التوضيحي
Path Variable ، على عكس جيرسي ، التي ، كما رأينا ، تستخدم
Path Param.
بهذا نختتم أسعارنا مع Spring RestController.
التوثيق
لقد حللنا كل هذه المشاكل من أجل إنشاء خدماتنا الدقيقة ، لكنها لن تحقق فائدة كافية إذا لم نعط العالم المعرفة حول كيفية استخدامها.
للقيام بذلك ، نستخدم أداة
Swagger المريحة والسهلة الاستخدام ، والتي لا توثق مكالمات API الخاصة بنا فحسب ، بل توفر أيضًا عميل ويب ملائم للاتصال بها.
أولاً ، دعنا نحدد Swagger في مجلدنا:
<dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger2</artifactId> <version>2.7.0</version> </dependency> <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger-ui</artifactId> <version>2.7.0</version> </dependency>
ثم نحتاج إلى إخبار Swagger بأي من الفصول الدراسية التي نريد توثيقها. دعونا نقدم فئة SwaggerConfig الجديدة التي تحتوي على مواصفات Swagger.
@Configuration @EnableSwagger2 public class SwaggerConfig { @Bean public Docket api() { return new Docket(DocumentationType.SWAGGER_2) .select() .apis(RequestHandlerSelectors.any()) .paths(PathSelectors.regex("/pricing.*")) .build(); } }
دعونا نرى ما يفعله هذا الفصل. أولاً ، قمنا بتعيين هذا كتكوين Swagger مع التعليق التوضيحي @ EnableSwagger2.
ثم أنشأنا مكون Docket يخبر Swagger عن واجهات برمجة التطبيقات التي يجب عرضها. في المثال أعلاه ، أخبرنا Swagger بإظهار أي مسار يبدأ بـ "/ التسعير". سيكون البديل تحديد فئات للتوثيق ، وليس للمسارات:
.apis(RequestHandlerSelectors.basePackage("com.restms.demo")) .paths(PathSelectors.any())
أعد تشغيل خدمة microservice بالأسعار واتصل
http: // localhost: 57216 / swagger-ui.html من المتصفح

انقر فوق قائمة العمليات لعرض عمليات الخدمة بالتفصيل.
انقر فوق توسيع العمليات لإنشاء طلب يستند إلى النموذج. قم بتعيين بعض المعلمات ، انقر فوق "جربها!" وانتظر الجواب:

يمكنك إضافة المزيد من الألوان بإضافة تعليقات Swagger التوضيحية إلى طرقك.
على سبيل المثال ، قم بتزيين أسلوب PricingImpl.getPricedPortfolio الحالي باستخدام التعليق التوضيحيApiOperation ، كما هو موضح أدناه:
@ApiOperation(value = "Retrieves a fully priced portfolio", notes = "Retrieves fully priced portfolio given customer id and portfolio id") @GetMapping("/customer/{customer-id}/portfolio/{portfolio-id}") public List<String> getPricedPortfolio(@PathVariable("customer-id") Integer customerId, @PathVariable("portfolio-id") Integer portfolioId)
قم بتحميل وتحديث واجهة مستخدم swagger-ui للاطلاع على الوثائق المحدثة الجديدة:

وهذا ليس كل ما يمكنك القيام به مع Swagger ، لذا تحقق من الوثائق.
يوري دفورزيتسكي ، المحاضر في دورتنا
"المطور في إطار الربيع" ، سيخبرك المزيد عن Spring Boot:
المقالة الأصلية