Membuat microservice di Quarkus, Kotlin dan Gradle


Pendahuluan


Pada artikel sebelumnya, kami menjelaskan secara singkat proses menciptakan microservice pada kerangka kerja JVM modern, serta perbandingannya. Artikel ini akan dibahas secara lebih terperinci dalam Quarkus yang baru-baru ini dirilis dengan contoh membuat layanan-mikro menggunakan teknologi yang disebutkan dan sesuai dengan persyaratan yang ditentukan dalam artikel utama. Aplikasi yang dihasilkan akan menjadi bagian dari arsitektur microservice berikut:


arsitektur target


Kode sumber proyek, seperti biasa, tersedia di GitHub .


Sebelum mulai bekerja dengan suatu proyek, berikut ini harus diinstal:



Buat proyek baru


Untuk menghasilkan proyek baru, gunakan web starter atau Maven (untuk membuat proyek Maven atau proyek Gradle ). Perlu dicatat bahwa kerangka mendukung bahasa Java, Kotlin dan Scala.


Kecanduan


Dalam proyek ini, Gradle Kotlin DSL digunakan sebagai sistem pembangunan. Skrip build harus berisi:


  • plugin
    Listing 1. build.gradle.kts


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

    Resolusi versi plugin dilakukan di settings.gradle.kts .


  • kecanduan
    Listing 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") ... } 

    Informasi tentang mengimpor Maven BOM tersedia di dokumentasi Gradle .



Anda juga perlu membuat beberapa kelas Kotlin open (mereka final secara default; informasi lebih lanjut tentang mengkonfigurasi Gradle dalam panduan Quarkus Kotlin :


Listing 3. build.gradle.kts


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

Konfigurasi


Kerangka kerja ini mendukung konfigurasi menggunakan properti dan file YAML
(lebih lanjut dalam panduan konfigurasi Quarkus ). File konfigurasi terletak di folder resources dan terlihat seperti ini:


Listing 4. application.yaml


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

Dalam fragmen kode ini, parameter standar dan khusus pada perangkat mikro dikonfigurasikan. Yang terakhir dapat dibaca seperti ini:


Listing 5. Membaca parameter aplikasi khusus ( kode sumber )


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

Tempat sampah


Sebelum Anda mulai bekerja dengan kode, perlu dicatat bahwa dalam kode sumber aplikasi Quarkus tidak ada metode utama (walaupun mungkin muncul ).


@ConfigProperties kacang @ConfigProperties dari daftar sebelumnya ke kacang lain dilakukan menggunakan anotasi @Inject :


Listing 6. Menerapkan kacang @ConfigProperties ( sumber )


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

Kacang ApplicationInfoService dijelaskan oleh @ApplicationScoped , pada gilirannya, dilaksanakan sebagai berikut:


Daftar 7. Menyebarkan kacang @ApplicationScoped ( sumber )


 class ApplicationInfoResource( @Inject private val applicationInfoService: ApplicationInfoService ) 

Informasi lebih lanjut tentang Konteks dan Injeksi Ketergantungan dalam panduan Quarkus CDI .


Kontroler SISA


Tidak ada yang tidak biasa dalam kontroler REST untuk mereka yang bekerja dengan Spring Framework atau Java EE:


Listing 8. Kontroler SISA ( sumber )


 @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() } 

REST client


Untuk bekerja dalam arsitektur microservice Quarkus, sebuah layanan harus dapat memenuhi permintaan untuk layanan lain. Karena semua layanan memiliki API yang sama, masuk akal untuk membuat antarmuka tunggal untuk kode umum dan sekelompok klien REST yang mewarisi itu:


Listing 9. REST klien ( kode sumber )


 @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 

Seperti yang Anda lihat, membuat klien REST untuk layanan lain hanya membuat antarmuka dengan anotasi JAX-RS dan MicroProfile yang sesuai.


Penemuan layanan


Seperti yang Anda lihat di bagian sebelumnya, nilai parameter baseUri adalah nama-nama layanan. Namun sejauh ini, Quarkus tidak memiliki dukungan bawaan untuk Layanan Penemuan ( Eureka ) atau tidak berfungsi ( Konsul ), karena kerangka kerjanya terutama ditujukan untuk bekerja di lingkungan cloud. Oleh karena itu, pola Penemuan Layanan diimplementasikan menggunakan Klien Konsul untuk perpustakaan Java .


Klien untuk Konsul mencakup dua metode, register dan getServiceInstance (menggunakan algoritma Round-robin):


Listing 10. Klien ke Konsul ( sumber )


 @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() } 

Pertama, Anda perlu mendaftarkan aplikasi:


Listing 11. Mendaftar ke Konsul ( sumber )


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

Selanjutnya, Anda perlu mengonversi nama layanan menjadi lokasi nyata. Untuk melakukan ini, gunakan kelas yang memperluas ClientRequestFilter dan @Provider beranotasi:


Listing 12. Filter untuk bekerja dengan Service Discovery ( sumber )


 @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 } } 

Filter hanya menggantikan URI objek requestContext dengan lokasi layanan yang diterima dari klien ke Konsul.


Pengujian


Tes untuk dua metode API diimplementasikan menggunakan perpustakaan REST Assured:


Listing 13. Tes ( sumber )


 @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())) } } 

Selama pengujian, tidak perlu mendaftar aplikasi dengan Konsul, oleh karena itu, dalam kode sumber proyek di sebelah pengujian adalah ConsulClientMock , yang memperluas ConsulClient :


Listing 14. Mock for ConsulClient ( sumber )


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

Majelis


Selama tugas build Gradle, tugas quarkusBuild . Secara default, ini menghasilkan JAR pelari dan folder lib mana dependensi berada. Untuk membuat uber-JAR, tugas quarkusBuild dikonfigurasi sebagai berikut:


Listing 15. Mengkonfigurasi generasi uber-JAR ( sumber )


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

Untuk membangun, jalankan ./gradlew clean build di akar proyek.


Luncurkan


Sebelum memulai microservice, Anda harus memulai Konsul (dijelaskan dalam artikel utama ).


Microservice dapat mulai menggunakan:


  • Tugas quarkusDev
    Jalankan pada akar proyek:
    ./gradlew :quarkus-service:quarkusDev
    atau jalankan tugas di IDE
  • uber-jar
    Jalankan pada akar proyek:
    java -jar quarkus-service/build/quarkus-service-1.0.0-runner.jar

Sekarang Anda dapat menggunakan REST API, misalnya, jalankan kueri berikut:


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


Hasilnya adalah:


Listing 16. Hasil dari panggilan API


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

Kompatibilitas Musim Semi


Kerangka kerja ini menyediakan kompatibilitas dengan beberapa teknologi Pegas: DI , Web , Keamanan , Data JPA .


Kesimpulan


Artikel itu meneliti contoh membuat layanan REST sederhana di Quarkus menggunakan Kotlin dan Gradle. Pada artikel utama, Anda dapat melihat bahwa aplikasi yang dihasilkan memiliki parameter yang sebanding dengan aplikasi pada kerangka kerja JVM modern lainnya. Dengan demikian, Quarkus memiliki pesaing serius, seperti Helidon MicroProfile, Micronaut dan Spring Boot (jika kita berbicara tentang kerangka kerja fullstack). Oleh karena itu, saya pikir kami sedang menunggu perkembangan acara yang menarik yang akan bermanfaat bagi seluruh ekosistem Jawa.


Tautan yang bermanfaat



PS Terima kasih vladimirsitnikov untuk bantuan dalam mempersiapkan artikel.

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


All Articles