Почему Kotlin — лучший выбор для микросервисов в 2024?
Вы когда-нибудь писали микросервис на Python, а потом думали: «А что, если бы он работал быстрее, потреблял меньше памяти и при этом код оставался таким же читаемым?» Знакомо? Тогда добро пожаловать в мир Kotlin — языка, который Google выбрал для Android, а сообщество backend-разработчиков — для создания легковесных и производительных микросервисов.
Kotlin идеально ложится на микросервисную архитектуру: он статически типизирован, компилируется в байт-код JVM (или нативный код через Kotlin/Native), но при этом не требует тонны бойлерплейта как Java. А главное — его корутины (coroutines) позволяют обрабатывать тысячи запросов на одном потоке без боли в виде callback hell.
В этой статье мы не будем читать лекции по теории. Мы возьмём и напишем два микросервиса: User Service (REST API) и Notification Service (имитация отправки писем). Всё на чистом Kotlin + Ktor. Поехали!
1. Инструментарий: что ставить перед стартом
Прежде чем писать код, давайте убедимся, что ваш компьютер готов к бою.
Что нужно установить:
- JDK 17+ — Kotlin работает на JVM, поэтому без Java никуда. Скачать можно с Adoptium.
- IntelliJ IDEA Community Edition — лучшая IDE для Kotlin (да, она от JetBrains, и да, она бесплатна).
- Gradle (или Maven) — система сборки. В IntelliJ он уже встроен, но если хотите ставить отдельно — gradle.org.
- Docker (опционально, но сильно упростит деплой).
Если у вас macOS или Linux — вы уже на полпути к успеху. На Windows рекомендую WSL2.
2. Создаём первый микросервис: User Service на Ktor
Ktor — это фреймворк от создателей Kotlin. Он максимально лёгкий: вы сами решаете, какие модули подключать (маршрутизация, сериализация, логирование). Никакой магии — только чистый код.
2.1. Генерация проекта
Самый быстрый способ — использовать Ktor Project Generator. Выберите:
- Build system: Gradle Kotlin DSL
- Engine: Netty (самый популярный)
- Plugins: Routing, Content Negotiation (JSON), Status Pages
Скачайте архив, откройте в IntelliJ — и через 2 минуты у вас готовый скелет.
2.2. Пишем REST API: получение пользователей
Создадим простую модель и эндпоинт. Код будет максимально читаемым:
// Model.kt
data class User(
val id: Int,
val name: String,
val email: String
)
// UserRepository.kt
object UserRepository {
private val users = mutableListOf(
User(1, "Alice", "alice@example.com"),
User(2, "Bob", "bob@example.com")
)
fun findAll(): List<User> = users
fun findById(id: Int): User? = users.find { it.id == id }
}
// Application.kt (точка входа)
fun main() {
embeddedServer(Netty, port = 8080) {
configureSerialization()
configureRouting()
}.start(wait = true)
}
// Routing.kt
fun Application.configureRouting() {
routing {
route("/api/v1/users") {
get {
call.respond(UserRepository.findAll())
}
get("/{id}") {
val id = call.parameters["id"]?.toIntOrNull()
if (id == null) {
call.respond(HttpStatusCode.BadRequest, "Invalid ID")
return@get
}
val user = UserRepository.findById(id)
if (user != null) {
call.respond(user)
} else {
call.respond(HttpStatusCode.NotFound, "User not found")
}
}
}
}
}
Обратите внимание: мы используем coroutines неявно — Ktor сам переводит каждый запрос в корутину. Никаких CompletableFuture или ручного управления потоками.
2.3. Подключаем базу данных (Exposed)
Хранить данные в памяти — весело, но ненадёжно. Подключим PostgreSQL через Exposed — ORM от JetBrains для Kotlin:
// build.gradle.kts
dependencies {
implementation("org.jetbrains.exposed:exposed-core:0.44.1")
implementation("org.jetbrains.exposed:exposed-dao:0.44.1")
implementation("org.jetbrains.exposed:exposed-jdbc:0.44.1")
implementation("org.postgresql:postgresql:42.6.0")
}
// DatabaseFactory.kt
object DatabaseFactory {
fun init() {
Database.connect(
url = "jdbc:postgresql://localhost:5432/users_db",
driver = "org.postgresql.Driver",
user = "postgres",
password = "password"
)
}
}
// UsersTable.kt
object UsersTable : Table("users") {
val id = integer("id").autoIncrement()
val name = varchar("name", 50)
val email = varchar("email", 100)
override val primaryKey = PrimaryKey(id)
}
// Теперь в Application.kt вызываем DatabaseFactory.init() перед стартом сервера
Готово! Теперь ваш User Service умеет читать данные из реальной БД.
3. Второй микросервис: Notification Service
Пусть наш второй сервис будет совсем маленьким — он будет принимать запросы от User Service и «отправлять» письма (просто писать в лог).
3.1. Создаём ещё один проект Ktor
Повторите шаги из п. 2.1, но назовите проект notification-service и поставьте порт 8081.
3.2. Эндпоинт для отправки уведомлений
// NotificationRoutes.kt
fun Application.notificationRoutes() {
routing {
post("/api/v1/notifications/send") {
val notification = call.receive<Notification>()
// Имитация отправки
log.info("Sending email to ${notification.email}: ${notification.message}")
call.respond(HttpStatusCode.OK, NotificationResponse("Sent successfully"))
}
}
}
data class Notification(
val email: String,
val message: String
)
data class NotificationResponse(val status: String)
3.3. Как сервисы общаются между собой?
В микросервисах два основных паттерна: синхронный (HTTP) и асинхронный (через брокер сообщений, например, RabbitMQ или Kafka). Для начала используем HTTP-клиент из Ktor:
// В User Service добавляем вызов Notification Service
suspend fun sendWelcomeEmail(user: User) {
val client = HttpClient(CIO) {
install(ContentNegotiation) { json() }