Long Polling مقابل WebSocket في Go: معركة الزمن الحقيقي

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

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

ابدأ الدورة

مقدمة: معضلة المطور

\n\n

تخيل: أنت تكتب دردشة، محطة تداول أسهم، أو لعبة على الإنترنت بلغة Go. يحتاج العميل إلى تلقي التحديثات فورًا. طريقة AJAX القديمة الجيدة مع الاستقصاء الدوري (polling) غير مناسبة — الكثير من الطلبات غير المفيدة التي تستنزف الخادم وبطارية المستخدم. يتبقى مرشحان: Long Polling (الاستقصاء الطويل) و WebSocket.

\n\n

كلاهما يحل مشكلة "الوقت الحقيقي"، لكنهما يفعلان ذلك بطرق مختلفة جوهريًا. أي منهما تختار لتطبيقك بلغة Go؟ دعنا نفهم دون حشو، مع أمثلة كود حية وتحليل صادق للإيجابيات والسلبيات. هذه المقالة هي دليلك في عالم الاتصالات الفورية بلغة Go.

\n\n

ما هو Long Polling في Go؟

\n\n

Long Polling هو تطور للاستقصاء العادي. يرسل العميل طلب HTTP إلى الخادم، لكن الخادم لا يرد فورًا إذا لم تكن هناك بيانات. إنه "يتجمد" (يبقي الاتصال مفتوحًا) وينتظر حتى يظهر حدث جديد. بمجرد حدوث الحدث — يرسل الخادم الرد ويقوم العميل فورًا بإنشاء طلب Long Polling جديد. تتكرر الدورة.

\n\n

في Go، يتم تنفيذ ذلك بأناقة شديدة بفضل goroutines والقنوات. يمكن للخادم "إسبات" معالج الطلب حتى تظهر البيانات.

\n\n

مثال لخادم Long Polling بلغة Go

\n\n
package main\n\nimport (\n    "fmt"\n    "net/http"\n    "time"\n)\n\n// قناة تصل إليها الرسائل الجديدة\nvar messages = make(chan string, 10)\n\nfunc longPollHandler(w http.ResponseWriter, r *http.Request) {\n    select {\n    case msg := <-messages:\n        fmt.Fprintf(w, "رسالة جديدة: %s", msg)\n    case <-time.After(30 * time.Second):\n        // مهلة — إذا لم تصل رسائل خلال 30 ثانية\n        http.Error(w, "Timeout", http.StatusGatewayTimeout)\n    }\n}\n\nfunc main() {\n    // Goroutine تحاكي ظهور رسائل جديدة\n    go func() {\n        for {\n            time.Sleep(5 * time.Second)\n            messages <- fmt.Sprintf("الوقت: %s", time.Now().String())\n        }\n    }()\n\n    http.HandleFunc("/poll", longPollHandler)\n    http.ListenAndServe(":8080", nil)\n}\n
\n\n

سيبدو العميل بلغة JavaScript كالتالي: بعد تلقي الرد (أو المهلة) يقوم فورًا باستدعاء /poll مرة أخرى. بسيط، موثوق، يعمل عبر أي بروكسي وجدار ناري.

\n\n

WebSocket في Go: الازدواج الكامل

\n\n

WebSocket هو بروتوكول، بعد المصافحة (HTTP Upgrade)، يحول الاتصال إلى قناة دائمة ثنائية الاتجاه. يمكن للخادم إرسال البيانات إلى العميل في أي لحظة، والعميل إلى الخادم. لا طلبات متكررة، لا مهلات.

\n\n

في Go، لا تحتوي المكتبة القياسية على دعم مدمج لـ WebSocket، لكن هناك المعيار الذهبي — حزمة gorilla/websocket. إنها مستقرة، موثقة جيدًا، وتستخدم في الإنتاج من قبل آلاف المشاريع.

\n\n

مثال لخادم WebSocket بلغة Go

\n\n
package main\n\nimport (\n    "log"\n    "net/http"\n    "time"\n\n    "github.com/gorilla/websocket"\n)\n\nvar upgrader = websocket.Upgrader{\n    CheckOrigin: func(r *http.Request) bool {\n        return true // للتطوير. في الإنتاج — تحقق!\n    },\n}\n\nfunc wsHandler(w http.ResponseWriter, r *http.Request) {\n    conn, err := upgrader.Upgrade(w, r, nil)\n    if err != nil {\n        log.Print("فشل الترقية:", err)\n        return\n    }\n    defer conn.Close()\n\n    // حلقة لا نهائية للقراءة والكتابة\n    for {\n        // نقرأ رسالة من العميل\n        _, message, err := conn.ReadMessage()\n        if err != nil {\n            log.Println("خطأ في القراءة:", err)\n            break\n        }\n        log.Printf("تم الاستلام: %s", message)\n\n        // نرسل ردًا (أو إشعارًا فوريًا)\n        err = conn.WriteMessage(websocket.TextMessage, []byte("الخادم: "+time.Now().String()))\n        if err != nil {\n            log.Println("خطأ في الكتابة:", err)\n            break\n        }\n    }\n}\n\nfunc main() {\n    http.HandleFunc("/ws", wsHandler)\n    log.Pr                

المدونات

توصيات الكتب