Long Polling مقابل WebSocket في Kotlin: أيهما تختار للوقت الفعلي
\n\nتخيل: أنت تكتب تطبيق دردشة في Kotlin، أو محطة تداول، أو نظام مراقبة خوادم. يحتاج العميل إلى رؤية التغييرات فورًا. طلب HTTP التقليدي "اطلب-ورد" لن يفي بالغرض هنا - يجب أن تصل البيانات من تلقاء نفسها، دون حاجة المستخدم إلى الضغط باستمرار.
\n\nفي عالم Kotlin، هناك طريقتان شائعتان: Long Polling و WebSocket. كلاهما يسمحان بتنظيم تفاعل في الوقت الفعلي، لكنهما يفعلان ذلك بشكل مختلف جوهريًا. أيهما تختار؟ دعنا نفهم ذلك من خلال أمثلة عملية، بدون حشو أو محاضرات أكاديمية.
\n\nما هو Long Polling (ولماذا لم يمت)
\n\nLong Polling هي تقنية يرسل فيها العميل طلب HTTP، ولا يرد الخادم فورًا، بل يظل "معلقًا" مفتوحًا حتى تظهر بيانات جديدة. بمجرد ظهور البيانات - يرسل الخادم الرد، يعالجه العميل ويفتح طلبًا جديدًا على الفور. تتكرر الدورة.
\n\nيبدو مثل HTTP عادي، ولكن مع مهلة زمنية طويلة. لا توجد بروتوكولات إضافية، ولا مصافحة - كل شيء يعمل فوق HTTP/1.1 أو HTTP/2 القياسي.
\n\nمثال Long Polling في Kotlin (خادم Ktor + عميل OkHttp)
\n\nجزء الخادم باستخدام Ktor (سينتظر الخادم حتى تظهر رسالة):
\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 // محاكاة انتظار رسالة جديدة\n val message = withContext(Dispatchers.Default) {\n delay(5000) // ننتظر 5 ثوانٍ (أو حدث جديد)\n "رسالة جديدة: ${System.currentTimeMillis()}"\n }\n call.respondText(message, ContentType.Text.Plain)\n }\n }\n }.start(wait = true)\n}\n\n\nالعميل باستخدام OkHttp:
\n\nimport okhttp3.*\n\nval client = OkHttpClient.Builder()\n .readTimeout(30, TimeUnit.SECONDS) // نقطة مهمة!\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 // يجب إعادة تشغيل polling عند حدوث خطأ\n startLongPolling()\n }\n\n override fun onResponse(call: Call, response: Response) {\n response.use {\n if (response.isSuccessful) {\n println("تم الاستلام: ${response.body?.string()}")\n // نبدأ الطلب التالي فورًا\n startLongPolling()\n }\n }\n }\n })\n}\n\n\nإيجابيات Long Polling:
\n- \n
- يعمل في كل مكان، حتى خلف البروكسيات المؤسسية وفي المتصفحات القديمة \n
- تصحيح الأخطاء بسيط - ترى طلبات HTTP عادية في السجلات \n
- سهولة التكامل مع البنية التحتية الحالية (موازنات التحميل، التخزين المؤقت، CDN) \n
سلبيات:
\n- \n
- زمن استجابة عالي - قد يمر وقت RTT (زمن الرحلة ذهابًا وإيابًا) بين إرسال الطلب واستلام الرد \n
- الخادم يحتفظ بالعديد من الاتصالات المفتوحة (كل طلب هو مؤشر ترابط/كوروتين منفصل) \n
- احتمال فقدان الرسائل عند إعادة الاتصال (إذا لم يتم تنفيذ خاصية التطابق) \n
WebSocket: ازدواج كامل
\n\nWebSocket هو بروتوكول منفصل (ws://) ينشئ اتصالًا ثنائي الاتجاه دائمًا بعد مصافحة HTTP. يمكن للخادم والعميل إرسال الرسائل في أي وقت، بدون حمل زائد للرؤوس.
\n\n