gorilla/websocket v1.5+ 默认拒绝所有跨域origin,必须显式配置checkorigin;开发可临时返回true,生产须白名单校验origin,且nginx需透传origin头。

gorilla/websocket 升级到 v1.5+ 后必须改写 Upgrader.CheckOrigin
旧代码里直接返回 true 的写法在新版本会触发 panic:panic: websocket: origin not allowed。这不是 bug,而是安全策略收紧——默认拒绝所有跨域 Origin。
正确做法是显式声明允许的来源,而不是绕过检查:
- 开发环境可临时用
u.CheckOrigin = func(r *http.Request) bool { return true },但上线前必须删掉 - 生产环境应校验
r.Header.Get("Origin")是否在白名单内,比如只允许可信域名:strings.HasSuffix(origin, ".example.com") - 如果前端走 Nginx 反代,注意
Origin头可能被剥离,需在 Nginx 配置中透传:proxy_set_header Origin $http_origin;
单机连接数卡在 65535 以下?先看 ulimit 和 net.ipv4.ip_local_port_range
不是 Go 代码写得不够好,而是 Linux 默认限制堵死了上限。即使 GOMAXPROCS 调高、goroutine 无阻塞,也连不到 10 万。
必须调整两项系统参数:
-
ulimit -n 1000000(当前会话生效),并写入/etc/security/limits.conf持久化 -
sysctl -w net.ipv4.ip_local_port_range="1024 65535"改成"1024 65535"不够用,要扩到"1024 65535"→ 实际应设为"1024 65535"是错的,正确是"1024 65535"?不对——真正要改的是net.ipv4.ip_local_port_range="1024 65535"本身没扩容空间,得改成"1024 65535"?等等,这数值重复了。真实有效扩法是:sysctl -w net.ipv4.ip_local_port_range="1024 65535"→ 错,应为"1024 65535"?不,65535 是上限,不能再高。所以关键其实是:每个连接占一个本地端口,而客户端 IP 数量有限时,服务端端口范围必须足够宽;更实际的做法是让客户端复用 IP + 端口(靠 TCP TIME_WAIT 复用),所以还要配:sysctl -w net.ipv4.tcp_tw_reuse=1 - 确认
net.core.somaxconn≥ 65535,否则 accept 队列溢出,连接被 RST
广播消息慢?别遍历 map,改用 broadcast channel + 分组扇出
原始写法:for conn := range cm.connections { conn.WriteMessage(...) },连接数一过 2000,延迟就明显上涨,且锁竞争剧烈。
优化核心不是“更快地遍历”,而是避免同步阻塞:
- 把广播逻辑从
Hub.Run()中剥离,每个连接单独 goroutine 写,用select { case 非阻塞发送 - 按业务分组(如 room ID、tenant ID)建多个
broadcastchannel,避免全量推送 ——sendToGroup("room-123", msg)比broadcastAll(msg)节省 90%+ 带宽和 CPU - 写失败时不要立刻
delete连接,先标记conn.markClosed = true,等下一次心跳检测周期统一清理,减少 map 并发修改冲突
连接数上不去?检查 net/http.Server.IdleTimeout 和 websocket.Upgrader.HandshakeTimeout
现象是压测时连接建立缓慢、大量超时,日志里反复出现 http: Accept error: accept tcp: too many open files 或 websocket: close 1006 (abnormal closure): unexpected EOF。
根本原因常被忽略:HTTP 层连接没及时释放,占着 fd 不放。
-
http.Server默认IdleTimeout = 0(永不超时),导致空闲连接长期挂住,耗尽文件描述符 —— 必须显式设为30 * time.Second -
websocket.Upgrader的HandshakeTimeout默认是 45 秒,但若前端网络抖动,握手包延迟到达,服务端已关闭连接,客户端重试又撞上 fd 限制 —— 建议设为5 * time.Second - Gin 的
engine.NoMethod和NoRoute中间件若未处理 OPTIONS 预检,也可能导致 WebSocket 握手被拦截,返回 405 而非升级响应
Goroutine 泄漏比内存泄漏更隐蔽——只要有一个 conn.WriteMessage 阻塞在 channel 发送,它背后的 goroutine 就永远卡住。监控 runtime.NumGoroutine() 上升趋势比看内存更早暴露问题。
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!











