WebSocket على Kotlin: إنشاء تطبيقات الوقت الفعلي المتقدمة

مدرب بايثون أونلاين للمبتدئين

تعلم Python بسهولة دون إرهاق نظري. حل مهام عملية مع التحقق التلقائي واكتب الكود مباشرة في المتصفح.

ابدأ الدورة

مقدمة: لماذا نحتاج إلى WebSocket متقدم على Kotlin؟

\n

تتطلب تطبيقات الويب الحديثة بشكل متزايد نقلًا فوريًا للبيانات: الدردشات، الإشعارات، الألعاب عبر الإنترنت، عروض الأسعار المالية ولوحات المراقبة. يتيح بروتوكول WebSocket الأساسي إنشاء اتصال ثنائي الاتجاه دائم بين العميل والخادم، ولكن في الممارسة العملية، يتطلب تطوير تطبيق وقت فعلي موثوق وقابل للتوسع على Kotlin أكثر بكثير من مجرد فتح socket.

\n

في هذه المقالة، سنتجاوز حدود خادم echo البسيط. سننظر في الأنماط المعمارية للعمل مع آلاف الاتصالات، ومعالجة الأخطاء باستخدام كوروتينات Kotlin، وتجميع الخوادم مع وسطاء الرسائل، وقضايا الأمان. المادة موجهة للمطورين الملمين بالفعل بأساسيات Kotlin و Ktor.

\n\n

1. هندسة خادم WebSocket عالي التحميل على Ktor

\n

بالنسبة لحل الإنتاج، لا يكفي مجرد تعليق معالج webSocket. من الضروري التفكير في إدارة الجلسات، ودورة حياة الاتصال، وعزل المعالجات. دعنا ننظر في العناصر الأساسية.

\n\n

إدارة الجلسات وتجميعها

\n

يتم تمثيل كل اتصال WebSocket في Ktor بواسطة كائن DefaultWebSocketSession. لإرسال الرسائل إلى مستخدم معين أو مجموعة (مثل "غرفة الدردشة")، تحتاج إلى تخزين الجلسات النشطة. استخدم ConcurrentHashMap لسلامة الخيوط.

\n
import io.ktor.websocket.*\nimport java.util.concurrent.ConcurrentHashMap\n\nclass SessionManager {\n    private val sessions = ConcurrentHashMap<String, WebSocketSession>()\n    private val roomSessions = ConcurrentHashMap<String, MutableSet<String>>()\n\n    fun addUserToRoom(roomId: String, userId: String, session: WebSocketSession) {\n        sessions[userId] = session\n        roomSessions.computeIfAbsent(roomId) { mutableSetOf() }.add(userId)\n    }\n\n    suspend fun broadcastToRoom(roomId: String, message: String) {\n        roomSessions[roomId]?.forEach { userId ->\n            sessions[userId]?.send(Frame.Text(message))\n        }\n    }\n\n    fun removeUser(userId: String) {\n        sessions.remove(userId)\n        roomSessions.forEach { (_, users) -> users.remove(userId) }\n    }\n}
\n\n

معالجة دورة الحياة باستخدام الكوروتينات

\n

يستخدم Ktor الكوروتينات لمعالجة كل اتصال. من المهم معالجة الاستثناءات وإغلاق socket بشكل صحيح لتجنب تسرب الذاكرة. استخدم try-catch داخل كتلة webSocket واستدعِ removeUser في كتلة finally.

\n
install(WebSockets) {\n    pingPeriod = Duration.ofSeconds(15)\n    timeout = Duration.ofSeconds(30)\n    maxFrameSize = Long.MAX_VALUE\n    masking = false\n}\n\nembeddedServer(Netty, port = 8080) {\n    val sessionManager = SessionManager()\n    routing {\n        webSocket("/chat/{roomId}") {\n            val roomId = call.parameters["roomId"] ?: "general"\n            val userId = call.request.queryParameters["userId"] ?: "anon"\n            sessionManager.addUserToRoom(roomId, userId, this)\n            try {\n                for (frame in incoming) {\n                    if (frame is Frame.Text) {\n                        val text = frame.readText()\n                        sessionManager.broadcastToRoom(roomId, "[$userId]: $text")\n                    }\n                }\n            } catch (e: Exception) {\n                log.error("WebSocket error for user $userId: ${e.localizedMessage}")\n            } finally {\n                sessionManager.removeUser(userId)\n                log.info("User $userId disconnected from room $roomId")\n            }\n        }\n    }\n}.start(wait = true)
\n\n

2. توسيع وتجميع WebSocket على Kotlin

\n

لن يتمكن خادم واحد من التعامل مع عشرات الآلاف من الاتصالات المتزامنة. الحل هو التوسع الأفقي. ومع ذلك، فإن اتصال WebSocket "ي

المدونات

توصيات الكتب