Golang最快WebSocket服务器Gws性能解析
Golang最快WebSocket服务器Gws性能解析
- 仓库:https://github.com/lxzan/gws
- 测试条件:1000 个连接,1000 * 1000 个请求,1000 字节负载,2 个 vCPU
- 测试代码:https://github.com/lxzan/go-websocket-testing
命令
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生态中最具性能优势的选择。

