在实时应用开发中,我们常需要同时提供 HTTP 接口(用于常规请求)和 WebSocket 服务(用于实时双向通信)。那 Go 语言能否高效兼顾这两者?答案是:完全可以,且实现异常简洁。
Go 原生 net/http 包可直接搭建 HTTP 服务,而 WebSocket 可通过成熟的第三方库实现协议升级,两者能共用一个端口、一个服务实例,无需额外部署,天生适配高并发场景。
核心原理:HTTP 握手升级为 WebSocket
WebSocket 协议的核心是“基于 HTTP 握手升级”——客户端先发送 HTTP 请求,携带 Upgrade: websocket 等头信息,服务端识别后将连接升级为 WebSocket 长连接,之后双方即可双向收发数据。
Go 中借助 gorilla/websocket 库(Go 生态最主流的 WebSocket 实现,稳定且易用)可快速完成升级,同时不影响原有 HTTP 服务的正常运行。
极简实现:一行命令+几十行代码
1. 依赖安装
仅需安装一个第三方库:
go get github.com/gorilla/websocket
2. 完整代码
精简后核心代码(保留双服务核心逻辑,可直接复制运行):
package main
import (
"log"
"net/http"
"github.com/gorilla/websocket"
)
var upgrader = websocket.Upgrader{CheckOrigin: func(r *http.Request) bool { return true }}
func handleWS(w http.ResponseWriter, r *http.Request) {
conn, err := upgrader.Upgrade(w, r, nil)
if err != nil { log.Println(err); return }
defer conn.Close()
for {
mt, msg, err := conn.ReadMessage()
if err != nil { break }
log.Printf("recv: %s", msg)
if err := conn.WriteMessage(mt, msg); err != nil { break }
}
}
func handleHTTP(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusOK)
w.Write([]byte("HTTP正常 | WebSocket: ws://localhost:8080/ws"))
}
func main() {
http.HandleFunc("/", handleHTTP)
http.HandleFunc("/ws", handleWS)
log.Fatal(http.ListenAndServe(":8080", nil))
}
3. 测试验证
-
测试 HTTP 服务:浏览器访问
http://localhost:8080,可看到状态提示。 -
测试 WebSocket 服务:用在线工具(如 WebSocket King)连接
ws://localhost:8080/ws,发送消息即可收到回声响应。
关键说明与扩展
1. 为何能同时提供服务?
Go 的 net/http 服务本质是“请求路由器”,不同路径对应不同处理函数:/ 路径走 HTTP 逻辑,/ws 路径触发 WebSocket 升级,两者共享同一个端口和服务进程,由 Go 原生的协程(Goroutine)高效处理并发连接,性能拉满。
2. 生产环境注意点
-
跨域限制:示例中
CheckOrigin: return true允许所有跨域请求,生产环境需替换为具体的域名校验逻辑(如判断r.Header.Get("Origin")是否在白名单内)。 -
连接管理:需维护 WebSocket 连接池(如用 map 存储连接),处理断线重连、心跳检测,避免无效连接占用资源。
-
性能优化:高并发场景可替换为
gobwas/ws库(内存占用更低,支持百万级连接),但gorilla/websocket足够覆盖绝大多数中小规模场景。
写在最后
Go 凭借原生 net/http 包的灵活性和第三方 WebSocket 库的成熟度,能轻松同时提供 HTTP 和 WebSocket 服务,代码简洁、并发能力强,非常适合开发实时聊天、监控面板、推送服务等场景。