إنشاء خدمة microservice على Quarkus و Kotlin و Gradle


مقدمة


في المقالة السابقة ، وصفنا بإيجاز عملية إنشاء خدمة microservice على أطر JVM الحديثة ، وكذلك مقارنتها. ستتم مناقشة هذه المقالة بمزيد من التفصيل في Quarkus الذي تم إصداره مؤخرًا من خلال مثال على إنشاء خدمة microservice باستخدام التقنيات المذكورة ووفقًا للمتطلبات المحددة في المقالة الرئيسية. سيصبح التطبيق الناتج جزءًا من بنية الخدمات المصغرة التالية:


العمارة المستهدفة


شفرة المصدر للمشروع ، كالعادة ، متاحة على جيثب .


قبل البدء في العمل في مشروع ، يجب تثبيت ما يلي:



إنشاء مشروع جديد


لإنشاء مشروع جديد ، استخدم بداية الويب أو Maven (لإنشاء مشروع Maven أو مشروع Gradle ). تجدر الإشارة إلى أن الإطار يدعم لغات Java و Kotlin و Scala.


zavismosti


في هذا المشروع ، يتم استخدام Gradle Kotlin DSL كنظام بناء. يجب أن يحتوي البرنامج النصي للبناء على:


  • الإضافات
    قائمة 1. build.gradle.kts


    plugins { kotlin("jvm") kotlin("plugin.allopen") id("io.quarkus") } 

    يتم إصدار دقة البرنامج المساعد في settings.gradle.kts .


  • اعتمادا على
    قائمة 2. build.gradle.kts


     dependencies { ... implementation(enforcedPlatform("io.quarkus:quarkus-bom:$quarkusVersion")) implementation("io.quarkus:quarkus-resteasy-jackson") implementation("io.quarkus:quarkus-rest-client") implementation("io.quarkus:quarkus-kotlin") implementation("io.quarkus:quarkus-config-yaml") testImplementation("io.quarkus:quarkus-junit5") ... } 

    تتوفر معلومات حول استيراد Maven BOM في وثائق Gradle .



يجب أيضًا أن تجعل بعض فئات Kotlin open (تكون final افتراضيًا ؛ مزيد من المعلومات حول تكوين Gradle في دليل Quarkus Kotlin :


قائمة 3. build.gradle.kts


 allOpen { annotation("javax.enterprise.context.ApplicationScoped") } 

تكوين


يدعم الإطار التكوين باستخدام الخصائص وملفات YAML
(المزيد في دليل التكوين Quarkus ). يوجد ملف التكوين في مجلد resources ويبدو كما يلي:


قائمة 4. application.yaml


 quarkus: http: host: localhost port: 8084 application-info: name: quarkus-service framework: name: Quarkus release-year: 2019 

في جزء التعليمات البرمجية هذا ، يتم تكوين المعلمات القياسية والعرف للجهاز microservice. يمكن قراءة هذا الأخير مثل هذا:


القائمة 5. قراءة معلمات التطبيق المخصص ( الكود المصدري )


 import io.quarkus.arc.config.ConfigProperties @ConfigProperties(prefix = "application-info") class ApplicationInfoProperties { lateinit var name: String lateinit var framework: FrameworkConfiguration class FrameworkConfiguration { lateinit var name: String lateinit var releaseYear: String } } 

بينا


قبل البدء في العمل باستخدام الكود ، تجدر الإشارة إلى أنه في الكود المصدر لتطبيق Quarkus لا توجد طريقة رئيسية (على الرغم من أنه قد يظهر ).


@ConfigProperties حبة @ConfigProperties من القائمة السابقة في حبة أخرى باستخدام التعليق التوضيحي @Inject :


@ConfigProperties قائمة 6. تنفيذ فول @ConfigProperties ( المصدر )


 @ApplicationScoped class ApplicationInfoService( @Inject private val applicationInfoProperties: ApplicationInfoProperties, @Inject private val serviceClient: ServiceClient ) { ... } 

ApplicationInfoService فول ApplicationInfoService المشروح بواسطة @ApplicationScoped ، بدوره ، على النحو التالي:


@ApplicationScoped 7. نشر الحبة @ApplicationScoped ( المصدر )


 class ApplicationInfoResource( @Inject private val applicationInfoService: ApplicationInfoService ) 

مزيد من المعلومات حول السياقات وحقن الاعتماد في دليل Quarkus CDI .


تحكم الراحة


لا يوجد شيء غير عادي في وحدة التحكم REST لأولئك الذين يعملون مع Spring Framework أو Java EE:


قائمة 8. تحكم REST ( المصدر )


 @Path("/application-info") @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION_JSON) class ApplicationInfoResource( @Inject private val applicationInfoService: ApplicationInfoService ) { @GET fun get(@QueryParam("request-to") requestTo: String?): Response = Response.ok(applicationInfoService.get(requestTo)).build() @GET @Path("/logo") @Produces("image/png") fun logo(): Response = Response.ok(applicationInfoService.getLogo()).build() } 

راحة العميل


للعمل في بنية Quarkus microservice ، يجب أن تكون الخدمة قادرة على تلبية طلبات الخدمات الأخرى. نظرًا لأن جميع الخدمات لها نفس واجهة برمجة التطبيقات ، فمن المنطقي إنشاء واجهة واحدة للرمز المشترك ومجموعة من عملاء REST يرثونها:


قائمة 9. عملاء REST ( شفرة المصدر )


 @ApplicationScoped @Path("/") interface ExternalServiceClient { @GET @Path("/application-info") @Produces("application/json") fun getApplicationInfo(): ApplicationInfo } @RegisterRestClient(baseUri = "http://helidon-service") interface HelidonServiceClient : ExternalServiceClient @RegisterRestClient(baseUri = "http://ktor-service") interface KtorServiceClient : ExternalServiceClient @RegisterRestClient(baseUri = "http://micronaut-service") interface MicronautServiceClient : ExternalServiceClient @RegisterRestClient(baseUri = "http://quarkus-service") interface QuarkusServiceClient : ExternalServiceClient @RegisterRestClient(baseUri = "http://spring-boot-service") interface SpringBootServiceClient : ExternalServiceClient 

كما ترون ، فإن إنشاء عميل REST للخدمات الأخرى هو مجرد إنشاء واجهة مع التعليقات التوضيحية JAX-RS و MicroProfile المقابلة.


اكتشاف الخدمة


كما رأيت في القسم السابق ، فإن baseUri معلمات baseUri هي أسماء الخدمات. لكن حتى الآن ، ليس لدى Quarkus دعمًا مدمجًا لـ Service Discovery ( Eureka ) أو أنه لا يعمل ( Consul ) ، لأن الإطار يهدف أساسًا إلى العمل في البيئات السحابية. لذلك ، يتم تطبيق نمط اكتشاف الخدمة باستخدام Consul Client لمكتبة Java .


يتضمن عميل القنصل طريقتين ، register و getServiceInstance (باستخدام خوارزمية Round-robin):


قائمة 10. العميل إلى القنصل ( المصدر )


 @ApplicationScoped class ConsulClient( @ConfigProperty(name = "application-info.name") private val serviceName: String, @ConfigProperty(name = "quarkus.http.port") private val port: Int ) { private val consulUrl = "http://localhost:8500" private val consulClient by lazy { Consul.builder().withUrl(consulUrl).build() } private var serviceInstanceIndex: Int = 0 fun register() { consulClient.agentClient().register(createConsulRegistration()) } fun getServiceInstance(serviceName: String): Service { val serviceInstances = consulClient.healthClient().getHealthyServiceInstances(serviceName).response val selectedInstance = serviceInstances[serviceInstanceIndex] serviceInstanceIndex = (serviceInstanceIndex + 1) % serviceInstances.size return selectedInstance.service } private fun createConsulRegistration() = ImmutableRegistration.builder() .id("$serviceName-$port") .name(serviceName) .address("localhost") .port(port) .build() } 

تحتاج أولاً إلى تسجيل التطبيق:


قيد 11. التسجيل مع القنصل ( المصدر )


 @ApplicationScoped class ConsulRegistrationBean( @Inject private val consulClient: ConsulClient ) { fun onStart(@Observes event: StartupEvent) { consulClient.register() } } 

بعد ذلك ، تحتاج إلى تحويل أسماء الخدمات إلى موقع حقيقي. للقيام بذلك ، استخدم فئة تمد ClientRequestFilter وشرحت @Provider :


قائمة 12. مرشح للعمل مع خدمة اكتشاف ( المصدر )


 @Provider @ApplicationScoped class ConsulFilter( @Inject private val consulClient: ConsulClient ) : ClientRequestFilter { override fun filter(requestContext: ClientRequestContext) { val serviceName = requestContext.uri.host val serviceInstance = consulClient.getServiceInstance(serviceName) val newUri: URI = URIBuilder(URI.create(requestContext.uri.toString())) .setHost(serviceInstance.address) .setPort(serviceInstance.port) .build() requestContext.uri = newUri } } 

يقوم عامل التصفية فقط باستبدال URI الخاص بالكائن requestContext مع موقع الخدمة الذي تم استلامه من العميل إلى القنصل.


تجريب


يتم تنفيذ اختبارات طريقتين API باستخدام مكتبة REST Assured:


قائمة 13. الاختبارات ( المصدر )


 @QuarkusTest class QuarkusServiceApplicationTest { @Test fun testGet() { given() .`when`().get("/application-info") .then() .statusCode(200) .contentType(ContentType.JSON) .body("name") { `is`("quarkus-service") } .body("framework.name") { `is`("Quarkus") } .body("framework.releaseYear") { `is`(2019) } } @Test fun testGetLogo() { given() .`when`().get("/application-info/logo") .then() .statusCode(200) .contentType("image/png") .body(`is`(notNullValue())) } } 

أثناء الاختبار ، ليست هناك حاجة لتسجيل التطبيق مع القنصل ، لذلك ، في الكود المصدري للمشروع بجانب الاختبار هو ConsulClientMock ، والذي يمتد ConsulClient :


القائمة 14. همية لـ ConsulClient ( مصدر )


 @Mock @ApplicationScoped class ConsulClientMock : ConsulClient("", 0) { // do nothing override fun register() { } } 

جمعية


أثناء مهمة build Gradle ، quarkusBuild مهمة quarkusBuild . بشكل افتراضي ، يقوم بإنشاء JAR عداء ومجلد lib حيث توجد التبعيات. لإنشاء uber-JAR ، quarkusBuild تكوين مهمة quarkusBuild على النحو التالي:


القائمة 15. تكوين جيل uber-JAR ( المصدر )


 tasks { withType<QuarkusBuild> { isUberJar = true } } 

لبناء ، تشغيل. / ./gradlew clean build في جذر المشروع.


إطلاق


قبل البدء في تشغيل خدمة microservice ، يجب أن تبدأ القنصل (الموضح في المقال الرئيسي ).


يمكن بدء استخدام Microservice:


  • مهمة quarkusDev
    تشغيل في جذر المشروع:
    ./gradlew :quarkus-service:quarkusDev
    أو قم بتشغيل المهمة في IDE
  • über بين JAR
    تشغيل في جذر المشروع:
    java -jar quarkus-service/build/quarkus-service-1.0.0-runner.jar

يمكنك الآن استخدام واجهة برمجة تطبيقات REST ، على سبيل المثال ، قم بتشغيل الاستعلام التالي:


GET http://localhost:8084/application-info


ستكون النتيجة:


سرد 16. نتيجة استدعاء API


 { "name": "quarkus-service", "framework": { "name": "Quarkus", "releaseYear": 2019 }, "requestedService": null } 

توافق الربيع


يوفر إطار العمل التوافق مع العديد من تقنيات Spring: DI ، Web ، Security ، Data JPA .


استنتاج


فحص المقال مثالًا على إنشاء خدمة REST بسيطة على Quarkus باستخدام Kotlin و Gradle. في المقال الرئيسي ، يمكنك أن ترى أن التطبيق الناتج يحتوي على معلمات قابلة للمقارنة مع التطبيقات على أطر JVM الحديثة الأخرى. وبالتالي ، لدى Quarkus منافسين جادين مثل Helidon MicroProfile و Micronaut و Spring Boot (إذا كنا نتحدث عن أطر fullstack). لذلك ، أعتقد أننا ننتظر تطورًا مثيرًا للاهتمام للأحداث التي ستكون مفيدة لنظام جافا البيئي بأكمله.


روابط مفيدة



سكرتير خاص شكرا vladimirsitnikov للمساعدة في إعداد المقال.

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


All Articles