Long Polling vs WebSocket en Kotlin: qué elegir para tiempo real
\n\nImagina: estás escribiendo un chat en Kotlin, un terminal bursátil o un sistema de monitoreo de servidores. El cliente necesita ver los cambios al instante. La solicitud HTTP tradicional de «ir y responder» no sirve aquí: los datos deben llegar solos, sin constantes «toques» del usuario.
\n\nEn el mundo de Kotlin hay dos enfoques populares: Long Polling y WebSocket. Ambos permiten organizar la interacción en tiempo real, pero lo hacen de formas fundamentalmente diferentes. ¿Cuál elegir? Analicémoslo con ejemplos prácticos, sin rodeos ni lecciones académicas.
\n\n¿Qué es Long Polling (y por qué no ha muerto)?
\n\nLong Polling es una técnica en la que el cliente envía una solicitud HTTP y el servidor no responde de inmediato, sino que «se queda abierto» hasta que aparecen nuevos datos. Tan pronto como los datos aparecen, el servidor envía la respuesta, el cliente la procesa e inmediatamente abre una nueva solicitud. El ciclo se repite.
\n\nParece HTTP normal, pero con un tiempo de espera largo. Sin protocolos adicionales, sin handshakes: todo funciona sobre HTTP/1.1 o HTTP/2 estándar.
\n\nEjemplo de Long Polling en Kotlin (Ktor Server + OkHttp Client)
\n\nParte del servidor en Ktor (el servidor esperará hasta que aparezca un mensaje):
\n\nimport io.ktor.server.engine.*\nimport io.ktor.server.netty.*\nimport io.ktor.server.routing.*\nimport io.ktor.server.response.*\nimport io.ktor.http.*\nimport kotlinx.coroutines.*\n\nfun main() {\n embeddedServer(Netty, port = 8080) {\n routing {\n get("/long-poll") {\n // Simulación de espera de un nuevo mensaje\n val message = withContext(Dispatchers.Default) {\n delay(5000) // esperamos 5 segundos (o un nuevo evento)\n "Nuevo mensaje: ${System.currentTimeMillis()}"\n }\n call.respondText(message, ContentType.Text.Plain)\n }\n }\n }.start(wait = true)\n}\n\n\nCliente con OkHttp:
\n\nimport okhttp3.*\n\nval client = OkHttpClient.Builder()\n .readTimeout(30, TimeUnit.SECONDS) // ¡punto importante!\n .build()\n\nfun startLongPolling() {\n val request = Request.Builder()\n .url("http://localhost:8080/long-poll")\n .build()\n\n client.newCall(request).enqueue(object : Callback {\n override fun onFailure(call: Call, e: IOException) {\n e.printStackTrace()\n // Obligatorio reiniciar el polling en caso de error\n startLongPolling()\n }\n\n override fun onResponse(call: Call, response: Response) {\n response.use {\n if (response.isSuccessful) {\n println("Recibido: ${response.body?.string()}")\n // Iniciamos inmediatamente la siguiente solicitud\n startLongPolling()\n }\n }\n }\n })\n}\n\n\nVentajas de Long Polling:
\n- \n
- Funciona en todas partes, incluso detrás de proxies corporativos y en navegadores antiguos \n
- Depuración simple: ves solicitudes HTTP normales en los registros \n
- Fácil de integrar en la infraestructura existente (balanceadores, cachés, CDN) \n
Desventajas:
\n- \n <>Alta latencia: entre el envío de la solicitud y la recepción de la respuesta puede pasar hasta un RTT (Round Trip Time)\n
- El servidor mantiene muchas conexiones abiertas (cada solicitud es un hilo/corutina separado) \n
- Al reconectarse, es posible la pérdida de mensajes (si no se implementa idempotencia) \n
WebSocket: dúplex completo
\n\nWebSocket es un protocolo separado (ws://) que establece una conexión bidireccional permanente después del handshake HTTP. El servidor y el cliente pueden enviar mensajes en cualquier momento, sin la sobrecarga de los encabezados.
\n\n