Golang最快WebSocket服务器Gws性能解析

Golang最快WebSocket服务器Gws性能解析

命令

tcpkali --connect-rate 500 \
    -c 1000 -r 1000 -T 30s -f ./assets/1K.txt \
    --ws 127.0.0.1:8000/connect

结果

gws:     5122.536↓, 5095.814↑ Mbps
gorilla: 1542.875↓, 1496.513↑ Mbps
nhooyr:  845.277↓,  814.357↑  Mbps
gobwas:  906.958↓,  909.138↑  Mbps

更多关于Golang最快WebSocket服务器Gws性能解析的实战教程也可以访问 https://www.itying.com/category-94-b0.html

1 回复

更多关于Golang最快WebSocket服务器Gws性能解析的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


gws的性能表现确实令人印象深刻,这主要得益于其底层的高效设计。从测试数据来看,gws的吞吐量显著高于其他主流WebSocket库,这归功于以下几个关键技术点:

1. 零拷贝设计

gws在消息处理时避免了不必要的内存分配和复制,直接操作网络缓冲区:

// gws内部使用零拷贝方式处理数据帧
func (c *Conn) readMessage() (opcode Opcode, data []byte, err error) {
    // 直接从网络缓冲区读取,不进行额外复制
    buf := c.readBuffer.Peek(headerSize)
    // 解析头部后,数据部分直接引用原始缓冲区
    payload := c.readBuffer.Next(payloadLen)
    return opcode, payload, nil
}

2. 优化的I/O多路复用

gws使用了更高效的epoll/kqueue系统调用,减少了上下文切换:

// gws的事件循环实现
func (s *Server) serve() error {
    for {
        n, err := s.poller.Wait(events)
        if err != nil {
            return err
        }
        for i := 0; i < n; i++ {
            conn := s.getConn(events[i].Fd)
            // 直接处理就绪的连接,避免遍历所有连接
            conn.handleEvent(events[i])
        }
    }
}

3. 内存池重用

gws大量使用sync.Pool来减少GC压力:

var framePool = sync.Pool{
    New: func() interface{} {
        return &Frame{
            Header: Header{},
            Payload: make([]byte, 0, 1024),
        }
    },
}

func getFrame() *Frame {
    return framePool.Get().(*Frame)
}

func putFrame(f *Frame) {
    f.Payload = f.Payload[:0]
    framePool.Put(f)
}

4. 批量写操作优化

gws将多个小消息合并为批量写入,减少系统调用次数:

// gws的批量写入实现
func (c *Conn) writeBuffers(buffers net.Buffers) error {
    // 使用writev系统调用一次性写入多个缓冲区
    n, err := c.writev(buffers)
    if err != nil {
        return err
    }
    // 更新写入统计
    atomic.AddInt64(&c.bytesWritten, int64(n))
    return nil
}

5. 性能对比分析

从测试结果可以看出性能差异的原因:

// gorilla/websocket的写入示例 - 每次写入都涉及内存分配
func (c *Conn) WriteMessage(messageType int, data []byte) error {
    // 内部会创建新的缓冲区并复制数据
    buf := make([]byte, len(data)+maxFrameHeaderSize)
    copy(buf[headerSize:], data)
    return c.writeBufs(buf)
}

// gws的写入示例 - 复用缓冲区
func (c *Conn) WriteMessage(opcode Opcode, data []byte) error {
    // 从池中获取帧,避免分配
    frame := getFrame()
    defer putFrame(frame)
    
    frame.Header.Opcode = opcode
    frame.Payload = append(frame.Payload[:0], data...)
    
    // 直接写入,无额外复制
    return c.writeFrame(frame)
}

6. 实际使用示例

以下是使用gws创建高性能WebSocket服务器的示例:

package main

import (
    "github.com/lxzan/gws"
    "net/http"
)

func main() {
    upgrader := gws.NewUpgrader(&Handler{}, &gws.ServerOption{
        ReadAsyncEnabled: true,    // 启用异步读取
        ParallelEnabled:  true,    // 启用并行处理
        CompressEnabled:  true,    // 启用压缩
        ReadBufferSize:   4096,    // 优化缓冲区大小
        WriteBufferSize:  4096,
    })
    
    http.HandleFunc("/connect", func(w http.ResponseWriter, r *http.Request) {
        socket, err := upgrader.Upgrade(w, r)
        if err != nil {
            return
        }
        go socket.ReadLoop()  // 启动读循环
    })
    
    http.ListenAndServe(":8000", nil)
}

type Handler struct{}

func (h *Handler) OnOpen(socket *gws.Conn) {
    // 连接建立
}

func (h *Handler) OnMessage(socket *gws.Conn, message *gws.Message) {
    // 处理消息 - 零拷贝访问数据
    data := message.Data.Bytes()
    // 直接回复,避免内存分配
    socket.WriteMessage(message.Opcode, data)
}

func (h *Handler) OnClose(socket *gws.Conn, err error) {
    // 连接关闭
}

gws的高性能主要来自其底层优化:零拷贝操作减少了内存分配,高效的I/O多路复用降低了系统调用开销,内存池重用减轻了GC压力,这些设计使其在同等测试条件下能够达到其他库3-5倍的吞吐量。对于需要处理大量并发WebSocket连接的应用场景,gws是目前Go生态中最具性能优势的选择。

回到顶部