You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Golang Gorilla WebSocket客户端无法加入Map池的负载测试问题

问题原因及解决办法

核心原因1:连接池的通道处理瓶颈

你的pool.Start()是单goroutine串行处理Connect和Disconnect事件,如果pool.Connect是无缓冲通道,serveWs函数中pool.Connect <- client会被阻塞,直到pool.Start()里的select接收这个客户端对象。当大量并发连接请求过来时,单goroutine的处理速度远跟不上连接创建的速度,导致后续的WebSocket升级请求被阻塞,无法建立新连接,最终表现为连接数停滞。

核心原因2:操作系统文件句柄限制

每个WebSocket连接本质是TCP连接,会占用一个系统文件句柄。Linux/macOS默认的进程文件句柄上限通常是1024(可通过ulimit -n查看),当服务端进程占用的文件句柄接近这个上限时,无法创建新的TCP连接,导致连接数无法继续增长。

原因3:k6客户端侧的资源限制

如果运行k6的机器本身存在CPU、内存或文件句柄限制,k6无法创建足够多的并发WebSocket客户端,也会导致服务端接收的连接数达不到预期。


解决办法

1. 优化连接池的通道处理

  • 给pool.Connect和pool.Disconnect通道添加缓冲:根据预期的并发峰值设置合理的缓冲大小,比如make(chan *Client, 1000),暂时容纳待处理的连接请求,避免serveWs被阻塞。
  • 并行处理连接事件:启动多个worker goroutine处理连接事件,提升处理效率。同时要给pool.Clients这个map添加互斥锁,避免并发读写的竞态问题:
    import "sync"
    
    type Pool struct {
        Clients     map[*Client]bool
        Connect     chan *Client
        Disconnect  chan *Client
        mu          sync.RWMutex // 添加互斥锁
    }
    
    func (pool *Pool) Start() {
        // 启动10个worker并行处理事件
        for i := 0; i < 10; i++ {
            go func() {
                for {
                    select {
                    case client := <-pool.Connect:
                        pool.mu.Lock()
                        pool.Clients[client] = true
                        pool.mu.Unlock()
                        fmt.Println("Size of Connection Pool: ", len(pool.Clients), " & ", client.Id)
                    case client := <-pool.Disconnect:
                        client.UnsubChan <- true
                        pool.mu.Lock()
                        delete(pool.Clients, client)
                        pool.mu.Unlock()
                        fmt.Println("Size of Connection Pool: ", len(pool.Clients), " & ", client.Id)
                    }
                }
            }()
        }
    }
    

2. 调整操作系统文件句柄限制

  • 临时调整:启动服务端前执行ulimit -n 65535(Linux/macOS),将进程文件句柄上限调高到65535。
  • 永久调整:Linux下修改/etc/security/limits.conf,添加以下内容后重启系统或重新登录:
    * soft nofile 65535
    * hard nofile 65535
    

3. 优化k6测试配置

  • 确保运行k6的机器有足够的CPU、内存资源,同时调高其文件句柄限制(和服务端调整方法一致)。
  • 合理设置k6的vus(虚拟用户数)和duration参数,避免瞬间耗尽客户端侧资源。

额外优化:WebSocket服务端的HTTP配置

显式调整Go的http.Server参数,避免默认配置限制连接数:

import "net/http"
import "time"

func main() {
    server := &http.Server{
        Addr:         ":8080",
        MaxOpenConns: 10000, // 设置最大打开连接数
        ReadTimeout:  30 * time.Minute, // 根据业务需求调整超时时间
        WriteTimeout: 30 * time.Minute,
    }
    // 注册WebSocket路由...
    server.ListenAndServe()
}

内容的提问来源于stack exchange,提问作者Suriya Varman N

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.21 12:18:24