مقدمة: معضلة المطور
\n\nتخيل: أنت تكتب دردشة، محطة تداول أسهم، أو لعبة على الإنترنت بلغة Go. يحتاج العميل إلى تلقي التحديثات فورًا. طريقة AJAX القديمة الجيدة مع الاستقصاء الدوري (polling) غير مناسبة — الكثير من الطلبات غير المفيدة التي تستنزف الخادم وبطارية المستخدم. يتبقى مرشحان: Long Polling (الاستقصاء الطويل) و WebSocket.
\n\nكلاهما يحل مشكلة "الوقت الحقيقي"، لكنهما يفعلان ذلك بطرق مختلفة جوهريًا. أي منهما تختار لتطبيقك بلغة Go؟ دعنا نفهم دون حشو، مع أمثلة كود حية وتحليل صادق للإيجابيات والسلبيات. هذه المقالة هي دليلك في عالم الاتصالات الفورية بلغة Go.
\n\nما هو Long Polling في Go؟
\n\nLong Polling هو تطور للاستقصاء العادي. يرسل العميل طلب HTTP إلى الخادم، لكن الخادم لا يرد فورًا إذا لم تكن هناك بيانات. إنه "يتجمد" (يبقي الاتصال مفتوحًا) وينتظر حتى يظهر حدث جديد. بمجرد حدوث الحدث — يرسل الخادم الرد ويقوم العميل فورًا بإنشاء طلب Long Polling جديد. تتكرر الدورة.
\n\nفي Go، يتم تنفيذ ذلك بأناقة شديدة بفضل goroutines والقنوات. يمكن للخادم "إسبات" معالج الطلب حتى تظهر البيانات.
\n\nمثال لخادم Long Polling بلغة Go
\n\npackage 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 مرة أخرى. بسيط، موثوق، يعمل عبر أي بروكسي وجدار ناري.
WebSocket في Go: الازدواج الكامل
\n\nWebSocket هو بروتوكول، بعد المصافحة (HTTP Upgrade)، يحول الاتصال إلى قناة دائمة ثنائية الاتجاه. يمكن للخادم إرسال البيانات إلى العميل في أي لحظة، والعميل إلى الخادم. لا طلبات متكررة، لا مهلات.
\n\nفي Go، لا تحتوي المكتبة القياسية على دعم مدمج لـ WebSocket، لكن هناك المعيار الذهبي — حزمة gorilla/websocket. إنها مستقرة، موثقة جيدًا، وتستخدم في الإنتاج من قبل آلاف المشاريع.
مثال لخادم WebSocket بلغة Go
\n\npackage 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