Long Polling vs WebSocket в Kotlin: что выбрать для реального времени

Онлайн-тренажер Python для начинающих

Изучайте Python легко и без перегрузки теорией. Решайте практические задачи с автоматической проверкой, получайте подсказки на русском языке и пишите код прямо в браузере — без необходимости что-либо устанавливать.

Начать курс

Long Polling vs WebSocket в Kotlin: что выбрать для реального времени

Представьте: вы пишете чат на Kotlin, биржевой терминал или систему мониторинга серверов. Клиенту нужно видеть изменения мгновенно. Традиционный HTTP-запрос «сходил-ответил» тут не подойдёт — данные должны приходить сами, без постоянных пинков от пользователя.

В мире Kotlin есть два популярных подхода: Long Polling и WebSocket. Оба позволяют организовать real-time взаимодействие, но делают это принципиально по-разному. Какой выбрать? Давайте разберёмся на живых примерах, без воды и академических лекций.

Что такое Long Polling (и почему он не умер)

Long Polling — это техника, при которой клиент отправляет HTTP-запрос, а сервер не отвечает сразу, а «висит» открытым до появления новых данных. Как только данные появляются — сервер отправляет ответ, клиент обрабатывает его и тут же открывает новый запрос. Цикл повторяется.

Выглядит как обычный HTTP, но с длинным таймаутом. Никаких дополнительных протоколов, никаких handshake’ов — всё работает поверх стандартного HTTP/1.1 или HTTP/2.

Пример Long Polling на Kotlin (Ktor Server + OkHttp Client)

Серверная часть на Ktor (сервер будет ждать, пока не появится сообщение):

import io.ktor.server.engine.*
import io.ktor.server.netty.*
import io.ktor.server.routing.*
import io.ktor.server.response.*
import io.ktor.http.*
import kotlinx.coroutines.*

fun main() {
    embeddedServer(Netty, port = 8080) {
        routing {
            get("/long-poll") {
                // Имитация ожидания нового сообщения
                val message = withContext(Dispatchers.Default) {
                    delay(5000) // ждём 5 секунд (или новое событие)
                    "Новое сообщение: ${System.currentTimeMillis()}"
                }
                call.respondText(message, ContentType.Text.Plain)
            }
        }
    }.start(wait = true)
}

Клиент на OkHttp:

import okhttp3.*

val client = OkHttpClient.Builder()
    .readTimeout(30, TimeUnit.SECONDS) // важный момент!
    .build()

fun startLongPolling() {
    val request = Request.Builder()
        .url("http://localhost:8080/long-poll")
        .build()

    client.newCall(request).enqueue(object : Callback {
        override fun onFailure(call: Call, e: IOException) {
            e.printStackTrace()
            // Обязательно перезапускаем polling при ошибке
            startLongPolling()
        }

        override fun onResponse(call: Call, response: Response) {
            response.use {
                if (response.isSuccessful) {
                    println("Получено: ${response.body?.string()}")
                    // Сразу запускаем следующий запрос
                    startLongPolling()
                }
            }
        }
    })
}

Плюсы Long Polling:

  • Работает везде, даже за корпоративными прокси и в старых браузерах
  • Простая отладка — вы видите обычные HTTP-запросы в логах
  • Легко встраивается в существующую инфраструктуру (балансировщики, кеши, CDN)

Минусы:

  • Высокая задержка — между отправкой запроса и получением ответа может пройти до RTT (Round Trip Time)
  • Сервер держит много открытых соединений (каждый запрос — отдельный поток/корутина)
  • При переподключении возможна потеря сообщений (если не реализовать идемпотентность)

WebSocket: полноценный дуплекс

WebSocket — это отдельный протокол (ws://), который устанавливает постоянное двунаправленное соединение после HTTP-рукопожатия. Сервер и клиент могут отправлять сообщения в любой момент, без оверхеда на заголовки.

Пример WebSocket на Kotlin (Ktor Server + Ktor Client)

Сервер:

import io.ktor.server.engine.*
import io.ktor.server.netty.*
import io.ktor.server.routing.*
import io.ktor.server.websocket.*
import io.ktor.websocket.*
import kotlinx.coroutines.delay

fun main() {
    embeddedServer(Netty, port = 8081) {
        install(WebSockets) // включаем поддержку WebSocket
        routing {
            webSocket("/chat") {
                send("Добро пожаловать в чат!")
                for (frame in incoming) {
                    if (frame is Frame.Text) {
                        val text = frame.readText()
                        println("Получено: $text")
                        send("Эхо: $text")
                    }
                }
            }
        }
    }.start(wait = true)
}

Клиент на Ktor Client:

import io.ktor.client.*
import io.ktor.client.plugins.websocket.*
import io.ktor.websocket.*
import kotlinx.coroutines.*

suspend fun main() {
    val client = HttpClient {
        install(WebSockets)
    }

    client.webSocket("ws://localhost:8081/chat") {
        send(Frame.Text("Привет, сервер!"))
        for (frame in incoming) {
            if (frame is Frame.Text) {
                println("Ответ: ${frame.readText()}")
            }
        }
    }
}

Плюсы WebSocket:

  • Минимальная задержка — данные летят мгновенно, без повторных handshake
  • Эффективное использование сети — нет заголовков HTTP на каждое сообщение
  • Поддержка фреймов (бинарные данные, текст, пинг/понг)

Минусы:

  • Не все прокси и фаерволы любят WebSocket (могут рвать соединение)
  • Сложнее с балансировкой — нужно sticky sessions или отдельный WebSocket gateway
  • Требуется явная обработка переподключения и heartbeat’ов

Когда что выбирать? Практические сценарии

Давайте честно: идеального решения нет. Всё зависит от конкретной задачи. Вот несколько типовых ситуаций:

Сценарий 1: Чат или мессенджер

Вердикт: WebSocket. Задержка критична, сообщения могут лететь пачками. Long Polling создаст лавину запросов при высокой нагрузке. Ktor WebSocket с корутинами справится отлично.

Сценарий 2: Панель мониторинга с обновлением раз в 10-30 секунд

Вердикт: Long Polling. Если данные меняются нечасто, WebSocket будет избыточен. Long Polling проще реализовать, он легче дебажится и не требует специальной инфраструктуры. Плюс вы можете использовать обычный HTTP-кеш.

Сценарий 3: Биржевой терминал или котировки в реальном времени

Вердикт: WebSocket. Тут важна каждая миллисекунда. Long Polling добавит задержку на каждый запрос, что неприемлемо для трейдинга. Используйте Ktor или OkHttp WebSocket.

Сценарий 4: Мобильное приложение с нестабильным интернетом

Вердикт: гибрид. Начните с WebSocket,

Блоги

Рекомендаций по книгам