Go语言中的Long Polling与WebSocket:实时通信之争

面向初学者的在线Python训练器

轻松学习Python,无需理论负担。在浏览器中直接解决带自动检查的实践任务,无需安装任何东西。

开始课程

引言:开发者的两难选择

\n\n

设想一下:您正在用Go编写一个聊天应用、股票交易终端或在线游戏。客户端需要即时获取更新。传统的AJAX轮询方式已经过时——会产生大量无用的请求,既拖垮服务器,又消耗用户电量。现在有两个候选方案:Long Polling(长轮询)和WebSocket。

\n\n

两者都能解决“实时”问题,但实现方式截然不同。对于您的Go应用程序,该如何选择?让我们通过真实的代码示例和诚实的优缺点分析来一探究竟。本文将是您在Go实时通信世界中的导航指南。

\n\n

Go中的Long Polling是什么?

\n\n

Long Polling是普通轮询的进化版。客户端向服务器发送HTTP请求,但如果服务器没有数据,它不会立即响应。它会“挂起”(保持连接打开),等待新事件发生。一旦事件发生,服务器发送响应,客户端立即发起一个新的Long Polling请求。如此循环往复。

\n\n

在Go中,由于goroutine和channel的存在,实现起来非常优雅。服务器可以在数据到达前“休眠”请求处理程序。

\n\n

Go Long Polling服务器示例

\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, "超时", 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

Go中的WebSocket:全双工通信

\n\n

WebSocket是一种协议,在握手(HTTP升级)后,将连接转换为永久的双向通道。服务器可以随时向客户端发送数据,客户端也可以随时向服务器发送数据。无需重复请求,没有超时。

\n\n

Go标准库没有内置的WebSocket支持,但有一个黄金标准——gorilla/websocket包。它稳定、文档完善,并被数千个项目用于生产环境。

\n\n

Go WebSocket服务器示例

\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                

博客

书籍推荐