Рубрики
Без рубрики

Развертывание приложения Kotlin в Heroku

С момента своего самого раннего выпуска Java рекламировала себя как программирование “пиши один раз, запускай везде”… Помеченный как java, kotlin, архитектура.

С момента своего самого раннего выпуска Java рекламировала себя как язык программирования ” пиши один раз, запускай везде “. Идея заключалась в том, что программист мог разработать приложение на Java, скомпилировать его до байт-кода и превратить в исполняемый файл, который может запускаться на любой платформе, независимо от операционной системы или платформы. Частично это удалось сделать с помощью среды выполнения, известной как виртуальная машина Java, или JVM .

К чести Java, JVM была (и остается!) невероятно точно настроенная среда выполнения, которая абстрагировалась от базового оборудования компьютера. Хотя Java как язык программирования существует и по сей день, его часто рассматривают как громоздкий, громоздкий и представляющий устаревший подход к внедрению решений.

За последние 10 лет появилось все больше и больше языков, работающих на JVM, но они выглядят и ощущаются совсем не так, как Java. Одним из таких языков является Kotlin . Из-за JVM он не имеет реальных преимуществ в производительности по сравнению с обычной Java. Тем не менее, его сила заключается в том факте, что он уделяет приоритетное внимание удобочитаемости так, как этого не делает Java. Рассмотрим, например, печать подстроки в Java:

// Java
String input = "What is the answer to the Ultimate Question of Life, the Universe, and Everything? 42";
String answer = input.substring(input.indexOf("?") + 1);
System.out.println(answer);

Сначала вы должны получить индекс символа, который вы хотите поместить в подстроку, добавить единицу (поскольку строки индексируются с нулевым индексом) и вызвать System.out.println для записи в stdout.

В Котлине это намного короче:

// Kotlin
val input = "What is the answer to the Ultimate Question of Life, the Universe, and Everything? 42"
val answer = input.substringAfter("?")
println(answer)

Котлин вызвал такой большой интерес, что Google даже рекомендует его вместо Java для разработки приложений для Android .

В этом посте мы кратко рассмотрим, как разработать приложение в Kotlin. Мы создадим простой API с базой данных PostgreSQL и развернем его в Heroku, чтобы увидеть его вживую.

Предпосылки

Прежде чем мы начнем, вам нужно убедиться, что на вашем компьютере установлено следующее программное обеспечение:

  1. Учетная запись на Heroku . Это абсолютно бесплатно и не требует никакой платежной информации.
  2. Интерфейс Heroku/| . Как только ваше приложение будет размещено на Heroku, это значительно упростит управление им. Вам нужно будет установить
  3. Kotlin .4). Вам также понадобится
  4. Gradle installed.0).

Вам также нужно будет немного ознакомиться с Git и установить его на свой компьютер.

Мы собираемся использовать IntelliJ IDEA для этого приложения Kotlin. Их документация содержит некоторые рекомендации о том, как создать новый проект. Убедитесь, что вы выбрали следующие параметры:

  • Мы хотим создать приложение Kotlin, которое использует Gradle в качестве системы сборки
  • Установите имя проекта на kotlin-api
  • Установите версию JDK равной 16. Если у вас не установлена эта версия, вы можете выбрать Загрузить JDK … из выпадающего списка, затем выберите Oracle OpenJDK версия 16

После того, как IDE все настроит, у вас должна быть структура каталогов, которая выглядит примерно так:

kotlin-api
├── build.gradle.kts
└── src
    ├── main
    │   ├── kotlin

Наши файлы Kotlin будут храниться в src/main/kotlin , а наша логика сборки будет находиться в build.gradle.kts .

Приступая к работе

Gradle – это инструмент сборки для различных языков. Он также действует как инструмент управления зависимостями, аналогичный Maven. У вас уже будет несколько строк в вашем файле build.gradle.kts , которые IDE автоматически добавила, чтобы быть полезными. Вы можете удалить все это и вставить вместо этого эти строки:

plugins {
    id("java")
    id("org.jetbrains.kotlin.jvm") version "1.5.10"
    id("org.springframework.boot") version "2.4.3"

    id("io.spring.dependency-management") version "1.0.11.RELEASE"
}

group "com.example"
version "0.0.1"

repositories {
    mavenCentral()
}

dependencies {
    implementation("org.jetbrains.kotlin:kotlin-stdlib") 

    implementation("org.springframework.boot:spring-boot-starter-web")    
    implementation("org.springframework.boot:spring-boot-starter")

    developmentOnly("org.springframework.boot:spring-boot-devtools")
}

В этих строках указаны зависимости вашего проекта и где их найти. Например, мы хотим использовать [org.springframework.boot](https://plugins.gradle.org/plugin/org.springframework.boot ) в версии 2.4.3, поэтому он определен в блоке plugins . Мы указываем репозитории, в которых можно найти пакеты — по адресу MavenCentral() — и какие открытые классы мы хотим использовать ( реализация ( "org.springframework.boot:spring-boot-starter-web") ).

Давайте создадим два небольших файла для тестирования нашей настройки. Создайте файл с именем Application.kt в папке src/main/kotlin и вставьте в него следующее:

package com.example

import org.springframework.boot.SpringApplication
import org.springframework.boot.autoconfigure.SpringBootApplication

@SpringBootApplication
open class Application

fun main(args: Array) {
    SpringApplication.run(Application::class.java, *args)
}

Это запускает приложение по умолчанию, использующее Spring framework. Настоящая магия происходит в следующем файле, Controller.kt , который вы должны создать вместе с Application.kt in src/main/kotlin :

package com.example

import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PathVariable
import org.springframework.web.bind.annotation.RestController

@RestController
class GreetingController {

    @GetMapping("/{name}")
    fun get(@PathVariable name: String) = "Hello, $name"
}

Здесь мы определяем маршрут ( @GetMapping("/{name}") ), где {name} является динамическим значением. Помещая этот декоратор поверх метода Kotlin ( fun get или “функция с именем get”), мы можем сопоставить маршрут с любым желаемым поведением — в данном случае возвращая приветствие с именем пути для запроса GET .

Для того, чтобы IDEA знала, как запустить наше приложение, нам нужно создать конфигурацию запуска . В верхней части меню IDE нажмите кнопку с надписью Добавить конфигурацию… . Выберите Добавьте новую конфигурацию запуска , затем выберите Gradle из списка. Для имени проекта Gradle введите kotlin-api. В поле Задачи введите Запуск загрузки . boot Run – это задача Gradle, предоставляемая Spring framework, которая скомпилирует наш код и запустит сервер. Нажмите Ok ; теперь у вас должна быть зеленая кнопка воспроизведения в строке меню IDE. Когда вы нажмете на это, IDE выполнит gradle bootRun для создания этого приложения Kotlin и запуска сервера. Когда это закончится, перейдите к http: //localhost:8080/world . Вы должны увидеть приятное приветствие.

Взаимодействие с базой данных

Теперь давайте перейдем к (несколько) серьезным вещам. Предположим, мы хотим присоединить базу данных к этому проекту. В мире Maven/Java нам нужно было бы обновить XML-файл и добавить ссылку на JAR-файл. В Gradle мы можем обойтись простым добавлением нескольких строк в наш файл build.gradle.kts :

dependencies {
    # ...

    implementation("com.zaxxer:HikariCP:4.0.3")
    runtimeOnly("org.postgresql:postgresql")

    # ...
}

Здесь мы включили HikariCP в нашем проекте, который является популярным драйвером подключения к базе данных. Мы также указываем, что хотим “загрузить” библиотеку org.postgresql во время выполнения. Всего этими двумя строками мы сообщили нашей конфигурации, что хотим взаимодействовать с базой данных PostgreSQL. Если у вас уже есть локальная база данных PostgreSQL, это здорово. Вы сможете продолжить остальную часть этого руководства локально и увидеть результаты при просмотре localhost. Если у вас нет PostgreSQL, не волнуйтесь — мы покажем вам, как легко развернуть это приложение на Heroku, которое позаботится об инфраструктуре за вас.

Возвращайтесь к Возвращайтесь к

package com.example
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PathVariable
import org.springframework.web.bind.annotation.RestController
import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.RequestBody
import org.springframework.http.MediaType
import com.zaxxer.hikari.HikariConfig
import com.zaxxer.hikari.HikariDataSource
import java.net.URI
import javax.sql.DataSource

@RestController
class GreetingController {

   val dataSource = dataSource()
   val connection = dataSource.connection

   @GetMapping("/{name}")

   fun get(@PathVariable name: String) = "Hello, $name"

   @PostMapping(value = ["/add-name"], consumes = [MediaType.TEXT_PLAIN_VALUE])
   fun post(@RequestBody requestBody: String) : String {
       initDb()
       val stmt = connection.createStatement()
       stmt.executeUpdate("INSERT INTO names values('$requestBody')")
       return "Added $requestBody"
   }

   @GetMapping("/everyone")

   fun getAll() : String {
       initDb()
       val stmt = connection.createStatement()
       val rs = stmt.executeQuery("SELECT name FROM names")
       val output = ArrayList()
       while (rs.next()) {
           output.add(rs.getString("name"))
       }
       val names = output.joinToString(", ")
       return "Here are the names: $names"
   }

   internal fun initDb() {
       val stmt = connection.createStatement()
       stmt.executeUpdate("CREATE TABLE IF NOT EXISTS names (name text)")
   }

   internal fun dataSource(): HikariDataSource {
       val config = HikariConfig()
       var dbUri = URI(System.getenv("DATABASE_URL") ?: "postgresql://localhost:5432/")
       var dbUserInfo =  dbUri.getUserInfo()
       var username: String?; var password: String?;
       if (dbUserInfo != null) {
           username = dbUserInfo.split(":").get(0)
           password = dbUserInfo.split(":").get(1)
       } else {
           username = System.getenv("DATABASE_USERNAME") ?: null
           password = System.getenv("DATABASE_PASSWORD") ?: null
       }
       if (username != null) {
           config.setUsername(username)
       }
       if (password != null) {
           config.setPassword(password)
       }
       val dbUrl = "jdbc:postgresql://${dbUri.getHost()}:${dbUri.getPort()}${dbUri.getPath()}"
       config.setJdbcUrl(dbUrl)
       return HikariDataSource(config)
   }
}

Здесь довольно много всего происходит! Давайте начнем с самого низа. Мы определяем функцию с именем DataSource , которая обеспечивает подключение к нашей базе данных. Поскольку мы создаем 12-факторное приложение , наши учетные данные базы данных хранятся в переменной среды с именем DATABASE_URL . Мы извлекаем этот URL-адрес и извлекаем из него имя пользователя и пароль, если таковые существуют. Если нет, мы проверяем еще две переменные среды на наличие этой информации — DATABASE_USERNAME и DATABASE_PASSWORD . Затем мы объединяем всю эту информацию в формат, необходимый соединителю базы данных. Функция initDb создает таблицу с именем names с одним текстовым столбцом с именем name . /каждый конечная точка имеет @GetMapping декоратор, как и раньше. Это определяет GET/everyone маршрут, который получает все имена из базы данных.

Наконец, мы добавили кое-что довольно новое: декоратор @PostMapping . Здесь нам нужно определить, какие типы контента это POST маршрут может быть принят. В этом случае он использует тип носителя TEXT_PLAIN_VALUE (другими словами, "Content-Type: text/plain" ). Любая строка информации, которую мы поместим в тело запроса, будет добавлена в базу данных. Всего за несколько строк мы создали небольшой API, который мы можем добавлять и запрашивать.

Если вы запустите этот сервер сейчас — и если у вас локально запущен PostgreSQL, — вы сможете взаимодействовать с ним. Попробуйте сделать следующий запрос:

$ curl -H "Content-Type: text/plain" -X POST http://localhost:8080/add-name -d 'Frank'

Если вы перейдете к http://localhost:8080/все , вы увидите, что Фрэнк был включен.

Развертывание в Heroku

Пришло время увидеть, насколько легко запустить Kotlin на Heroku. Во-первых, нам нужно создать файл, специфичный для Heroku: файл Proc . Этот текстовый файл определяет, как наше приложение должно загружаться и запускаться.

Создайте файл с именем Procfile в каталоге корневого уровня, прямо рядом с вашим файлом build.gradle.kts . Скопируйте и вставьте в него следующие строки:

web: java -jar build/libs/kotlin-api.jar --server.port=$PORT

Здесь мы говорим, что хотим, чтобы Heroku запускал java -jar build/libs/kotlin-api.jar . Этот JAR упаковывается и создается в процессе развертывания; Heroku автоматически создаст его для нас, потому что он знает, как выполнить задачу Gradle для этого. Мы также привязываем переменную окружения $PORT , чтобы Heroku знал, какой порт прослушивает сервер.

Затем выполните следующие команды Git:

$ git init
$ git add .
$ git commit -m "Preparing my first Kotlin app"

Поскольку у нас установлен интерфейс Heroku CLI, мы можем вызвать heroku create в командной строке для создания приложения :

$ heroku create
Creating app... done, ⬢ desolate-plains-67007
Created http://desolate-plains-67007.herokuapp.com/ | git@heroku.com:desolate-plains-67007.git

Вашему приложению будет присвоено случайное имя — в этом примере это desolate-plains-67007 — а также общедоступный URL-адрес.

Чтобы предоставить базу данных, мы используем команду heroku addons . Вызов его без аргументов даст нам знать, существуют ли таковые:

$ heroku addons
No add-ons for app desolate-plains-67007.

Для нашего приложения не существует никаких дополнений, что имеет смысл – мы только что создали его! Чтобы добавить базу данных PostgreSQL, мы можем использовать команду addons:create следующим образом:

$ heroku addons:create heroku-postgresql:hobby-dev

Heroku предлагает несколько уровней баз данных PostgreSQL. hobby-dev – это бесплатный уровень, так что мы можем поиграть с этим, не заплатив ни копейки.

Ваш код готов, ваше приложение Heroku настроено — вы готовы к развертыванию. Это самая легкая часть! Просто введите следующую команду:

$ git push heroku master

Ваш код будет отправлен в Heroku. С этого момента Хероку возьмет верх. Вы увидите, как ваши журналы сборки прокручиваются в вашем терминале. Это покажет вам, что Heroku устанавливает от вашего имени и где вы находитесь в процессе сборки. После его завершения вы можете перейти по своему специальному URL-адресу в браузере (в данном случае https://desolate-plains-67007.herokuapp.com ) и взаимодействовать с API в Интернете!

Узнавать больше

Производительный дизайн Kotlin и разборчивый синтаксис в сочетании с простотой Gradle делают его идеальным для предприятий, которым необходимо полагаться на проверенные в боях пакеты Java. Благодаря своей совместимости с Java Kotlin идеально подходит в качестве переходного языка; огромные объемы кода Java могут быть преобразованы в Kotlin, сохраняя при этом функциональное приложение. Развертывание в Heroku проходит гладко, и я даже не воспользовался различными способами тонкой настройки приложений на основе Java и JVM для развертывания .

Оригинал: “https://dev.to/heroku/deploying-a-kotlin-app-to-heroku-5d0g”