前言
大家好这里是,白泽,这期分析一下 golang 开源高性能 websocket 库 gws。
视频讲解请关注📺B站:白泽talk
介绍
- gws:https://github.com/lxzan/gws |GitHub 🌟 1.2k,高性能的 websocket 库,代码双语注释,适合有开发经验的同学进阶学习。
- gws 的两个特性
-
High IOPS Low Latency(高I/O,低延迟)
-
Low Memory Usage(低内存占用)
可以从下图看到: payload 越高,性能相比其他 websocket 库越是优越,如何做到?
gws chatroom 架构图
这是 gws 的官方聊天室 demo 的架构图,绘制在这里帮助各位理解什么是全双工的通信模式。
WebSocket 与 HTTP 一样是应用层的协议,只需要 TCP 完成三次握手之后,Golang 的 net/http 库提供了 Hijack() 方法,将 TCP 套接字(活跃的一个会话),从 HTTP 劫持,此后 tcp 的连接将由 WebSocket 管理,脱离了 HTTP 协议的范畴。
而只要获取了 TCP 的套接字,何时发送和接受数据,都是由应用层决定的,传输层的 TCP 套接字只是被编排的对象(单工/双工),自然可以实现服务端主动发送数据。
缓冲池
为什么 payload 越高,性能相比其他 websocket 库越是优越?
原因:gws 中的读写操作,全部使用了缓冲池。
binaryPool = internal.NewBufferPool(128, 256*1024) // 缓冲池
读缓冲:每次读取是一次系统调用,因此可以读取一段数据,且用一个 offset 定位消费的位置,减少读取次数。
写缓冲:每次写入是一次系统调用,因此可以多次写入 buffer,统一 flush。
缓冲池:为不同大小的 buffer 提供了缓冲池,大段 buffer 的创建次数减少,减少 GC 压力 & 创建对象和销毁对象时间。
// NewBufferPool Creating a memory pool
// Left, right indicate the interval range of the memory pool, they will be transformed into pow(2,n)。
// Below left, Get method will return at least left bytes; above right, Put method will not reclaim the buffer.
func NewBufferPool(left, right uint32) *BufferPool {
var begin, end = int(binaryCeil(left)), int(binaryCeil(right))
var p = &BufferPool{
begin: begin,
end: end,
shards: map[int]*sync.Pool{
},
}
for i := begin; i <= end; i *= 2 {
capacity := i
p.shards[i] = &sync.Pool{
New: func() any