Pull to refresh
998.53
OTUS
Цифровые навыки от ведущих экспертов

Kotlin-сервер без JVM — реальность?

Level of difficultyEasy
Reading time4 min
Views5.4K

Не секрет, что Kotlin может использоваться для создания всех компонентов FullStack-приложения - от мобильных приложения для Android/iOS и веб-сайтов на Kotlin JS до бэкэнда (например, с использованием Ktor, http4k и micronaut). Но все же многих останавливает от использования Kotlin для создания API тот факт, что код запускается в хоть и оптимизированной, но все же Java виртуальной машине. Есть ли решение у этой проблемы? Да, и в этой статье мы обсудим способы компиляции приложения на Kotlin для создания API в нативный код и подводные камни, которые нас ожидают на этом пути.

Прежде всего начнем с того, как Kotlin-код может быть преобразован в исполняемый файл для Windows/Linux или MacOS. В действительности компиляции в двоичный образ выполняется в два этапа и здесь используются возможности LLVM и специальные реализации frontend (компиляция Kotlin в промежуточное представление IR) и backend (компиляция IR в двоичный файл). Благодаря двухэтапной компиляции возможно разделить этап синтаксического анализа и компиляции кода в некоторое универсальное представление и дальнейшее преобразование под конкретную целевую платформу. Более подробно некоторые аспекты разработки на Kotlin Native для desktop-приложений мы рассматривали в этой статье.

С точки зрения конфигурирования единственное отличие Kotlin Native-модуля в multiplatform-приложении от JVM-приложения будет другая целевая платформа в блоке kotlin в build.gradle. Например, для сборки под  Linux конфигурация будет описана в linuxX64. Также можно использовать информацию о текущей платформе из System.getProperty("os.name"):

plugins {
    kotlin("multiplatform") version "1.8.0"
}

group = "org.example"
version = "1.0-SNAPSHOT"

repositories {
    mavenCentral()
}

kotlin {
    val hostOs = System.getProperty("os.name")
    val isMingwX64 = hostOs.startsWith("Windows")
    val nativeTarget = when {
        hostOs == "Mac OS X" -> macosX64("native")
        hostOs == "Linux" -> linuxX64("native")
        isMingwX64 -> mingwX64("native")
        else -> throw GradleException("Host OS is not supported in Kotlin/Native.")
    }
    nativeTarget.apply {
        binaries {
            executable()
        }
    }
    //определим также расположение исходных текстов модулей
    sourceSets {
        val nativeMain by getting {
            //здесь будут зависимости и конфигурация сборки
        }
        val nativeMain by getting
    }
}

Веб-сервер Ktor может использоваться в native target с некоторыми ограничениями, прежде всего возможно использовать только движок CIO и отсутствует поддержка SSL непосредственно в процессе сервера (но он может быть выполнен через reverse proxy). Добавим необходимые зависимости (для веб-сервера и тестирования) и попробуем создать простую маршрутизацию:

    sourceSets {
        val nativeMain by getting {
            dependencies {
                implementation("io.ktor:ktor-server-core:2.2.3")
                implementation("io.ktor:ktor-server-cio:2.2.3")
            }
        }
        val nativeTest by getting {
            dependencies {
                implementation("io.ktor:ktor-server-test-host:2.2.3")
                implementation("org.jetbrains.kotlin:kotlin-test:1.8.0")
            }
        }    
    }

Исходный текст сервера:

import io.ktor.server.application.*
import io.ktor.server.cio.*
import io.ktor.server.engine.*
import io.ktor.server.response.*
import io.ktor.server.routing.*

fun main() {
    embeddedServer(CIO, port = 8080) {
        module()
    }.start(wait = true)
}

fun Application.module() {
    routing {
        get("/") {
            call.respondText("Hello")
        }
    }
}

И сразу создадим тест для него:

import io.ktor.client.request.*
import io.ktor.client.statement.*
import io.ktor.http.*
import io.ktor.server.testing.*
import kotlin.test.Test
import kotlin.test.assertEquals

class ApplicationTest {
    @Test
    fun testRoot() = testApplication {
        application {
            module()
        }
        val response = client.get("/")
        assertEquals(HttpStatusCode.OK, response.status)
        assertEquals("Hello", response.bodyAsText())
    }
}

Для запуска сервера будем использовать gradle‑задачу runDebugExecutableNative (также создаст исполняемый файл в формате целевой платформы в build/bin/native/debugExecutable *.kexe). При запуске тестов (gradle nativeTest) также будет создаваться исполняемый файл, в который интегрируется Native Test Runner и результат компиляции исходного приложения и тестового сценария (build/bin/native/debugTest/test.kexe).

Для корректной работы ktor требуется новая модель памяти (включена по умолчанию в Kotlin 1.7.20 и более новых). Также нам будет доступна возможность использования всех функций и расширений из стандартной библиотеки Kotlin (включая корутины, поддержку коллекций и др.).

В реальной ситуации для веб‑сервера вряд ли будет достаточно возвращать константные строки и в минимальном варианте будет необходимо использовать библиотеки для сериализации JSON (для нативной разработки можно использовать kotlinx.serialization). Также можно использовать библиотеки, которые имеют вариант сборки для нативной платформы или любые библиотеки, не использующие какие‑либо специфичные для Java классы и методы. Список библиотек например можно посмотреть здесь или здесь.

Из расширений ktor для native-сервера можно использовать следующие:

  • HTML/CSS DSL

  • Basic Authentication

  • Bearer Authentication

  • Form Based Authentication

  • Sessions

  • Caching Headers, Conditional Headers

  • CORS

  • HSTS

  • Partial Content

  • Data conversion (с kotlinx.serialization)

  • WebSockets

  • и ряд других

Для библиотек, которые не представлены в native target, можно попробовать интегрировать исходными текстами, либо реализовать самостоятельно (для этого можно использовать Ktor Client для сетевых запросов и все возможности стандартной библиотеки Kotlin, включая сериализацию). Также существуют библиотеки для организации Key-Value хранилищ (например, multiplatform-settings, Kodein-DB, KStore или KVault) и DI (Kodein, Koin, Inject) и количество библиотек постепенно увеличивается.

Основной проблемой реализации реальных веб-серверов в Kotlin Native на данный момент являются сложности с поддержкой драйверов для баз данных, однако это может быть решено через создание (или использование готовых исходных кодов) библиотек на Kotlin с заменой сетевого слоя с библиотек Java на использование Ktor Client или аналогичных мультиплатформенных клиентов. В остальном создание нативных реализаций серверов доступно уже сейчас и для высокопроизводительных задач можно рассматривать сочетание Kotlin Native + Ktor + KStore для хранения данных.

Завершить статью хочу приглашением на полезный бесплатный урок.

Тема урока: "Полноценное приложение на Kotlin с нуля описание".

В процессе рассмотрим интересные технологии из сферы Kotlin, из-за чего язык становится мощным и удобным инструментом. Создадим мультиплатформенный проект, на Kotlin/JVM и Kotlin/JS, напишем фронтенд с помощью удобных DSL для React и CSS. Воспользуемся корутинами для клиент-серверного взаимодействия.

Tags:
Hubs:
If this publication inspired you and you want to support the author, do not hesitate to click on the button
Total votes 13: ↑12 and ↓1+13
Comments4

Articles

Information

Website
otus.ru
Registered
Founded
Employees
101–200 employees
Location
Россия
Representative
OTUS