高性能 websocket 库 quickws 发布

290 天前
 guonaihong

quickws 是一个高性能的 websocket 库

地址

https://github.com/antlabs/quickws

example

服务端


package main

import (
	"fmt"
	"net/http"
	"time"
	"github.com/antlabs/quickws"
)

type echoHandler struct{}

func (e *echoHandler) OnOpen(c *quickws.Conn) {
	fmt.Println("OnOpen:", c)
}

func (e *echoHandler) OnMessage(c *quickws.Conn, op quickws.Opcode, msg []byte) {
	fmt.Println("OnMessage:", c, msg, op)
	if err := c.WriteTimeout(op, msg, 3*time.Second); err != nil {
		fmt.Println("write fail:", err)
	}
}

func (e *echoHandler) OnClose(c *quickws.Conn, err error) {
	fmt.Println("OnClose:", c, err)
}

// echo 测试服务
func echo(w http.ResponseWriter, r *http.Request) {
	c, err := quickws.Upgrade(w, r, quickws.WithServerReplyPing(),
		// quickws.WithServerDecompression(),
		// quickws.WithServerIgnorePong(),
		quickws.WithServerCallback(&echoHandler{}),
		quickws.WithServerReadTimeout(5*time.Second),
	)
	if err != nil {
		fmt.Println("Upgrade fail:", err)
		return
	}

	c.StartReadLoop()
}

func main() {
	http.HandleFunc("/", echo)

	http.ListenAndServe(":9001", nil)
}

常见问题

1.为什么 quickws 不标榜 zero upgrade?

第一:quickws 是基于 std 的方案实现的 websocket 协议。

第二:原因是 zero upgrade 对 websocket 的性能提升几乎没有影响(同步方式),所以 quickws 就没有选择花时间优化 upgrade 过程,

直接基于 net/http ,websocket 的协议是整体符合大数定律,一个存活几秒的 websocket 协议由 upgrade(握手) frame(数据包) frame frame 。。。组成。

所以随着时间的增长, upgrade 对整体的影响接近于 0 ,我们用数字代入下。

A: 代表 upgrade 可能会慢点,但是 frame 的过程比较快,比如基于 net/http 方案的 websocket

upgrade (100ms) frame(10ms) frame(10ms) frame(10ms) avg = 32.5ms

B: 代表主打 zero upgrade 的库,假如 frame 的过程处理慢点,

upgrade (90ms) frame(15ms) frame(15ms) frame(15ms) avg = 33.75ms

简单代入下已经证明了,决定 websocket 差距的是 frame 的处理过程,无论是 tps 还是内存占用 quickws 在实战中也会证明这个点。所以没有必须也不需要在 upgrade 下功夫,常规优化就够了。

2.quickws tps 如何

在 5800h 的 cpu 上面,tps 稳定在 47w/s ,接近 48w/s 。比 gorilla 使用 ReadMessage 的 38.9w/s ,快了近 9w/s

quickws.1:
1s:357999/s 2s:418860/s 3s:440650/s 4s:453360/s 5s:461108/s 6s:465898/s 7s:469211/s 8s:470780/s 9s:472923/s 10s:473821/s 11s:474525/s 12s:475463/s 13s:476021/s 14s:476410/s 15s:477593/s 16s:477943/s 17s:478038/s
gorilla-linux-ReadMessage.4.1 
1s:271126/s 2s:329367/s 3s:353468/s 4s:364842/s 5s:371908/s 6s:377633/s 7s:380870/s 8s:383271/s 9s:384646/s 10s:385986/s 11s:386448/s 12s:386554/s 13s:387573/s 14s:388263/s 15s:388701/s 16s:388867/s 17s:389383/s
gorilla-linux-UseReader.4.2:
1s:293888/s 2s:377628/s 3s:399744/s 4s:413150/s 5s:421092/s 6s:426666/s 7s:430239/s 8s:432801/s 9s:434977/s 10s:436058/s 11s:436805/s 12s:437865/s 13s:438421/s 14s:438901/s 15s:439133/s 16s:439409/s 17s:439578/s 
gobwas.6:
1s:215995/s 2s:279405/s 3s:302249/s 4s:312545/s 5s:318922/s 6s:323800/s 7s:326908/s 8s:329977/s 9s:330959/s 10s:331510/s 11s:331911/s 12s:332396/s 13s:332418/s 14s:332887/s 15s:333198/s 16s:333390/s 17s:333550/s

3.quickws 流量测试数据如何 ?

在 5800h 的 cpu 上面, 同尺寸 read buffer(4k), 对比默认用法,quickws 在 30s 处理 119GB 数据,gorilla 处理 48GB 数据。

quickws.windows.tcp.delay.4x:
Destination: [127.0.0.1]:9000
Interface lo address [127.0.0.1]:0
Using interface lo to connect to [127.0.0.1]:9000
Ramped up to 10000 connections.
Total data sent:     119153.9 MiB (124941915494 bytes)
Total data received: 119594.6 MiB (125404036361 bytes)
Bandwidth per channel: 6.625⇅ Mbps (828.2 kBps)
Aggregate bandwidth: 33439.980↓, 33316.752↑ Mbps
Packet rate estimate: 3174704.8↓, 2930514.7↑ (9↓, 34↑ TCP MSS/op)
Test duration: 30.001 s.
gorilla-linux-ReadMessage.tcp.delay:
WARNING: Dumb terminal, expect unglorified output.
Destination: [127.0.0.1]:9003
Interface lo address [127.0.0.1]:0
Using interface lo to connect to [127.0.0.1]:9003
Ramped up to 10000 connections.
Total data sent:     48678.1 MiB (51042707521 bytes)
Total data received: 50406.2 MiB (52854715802 bytes)
Bandwidth per channel: 2.771⇅ Mbps (346.3 kBps)
Aggregate bandwidth: 14094.587↓, 13611.385↑ Mbps
Packet rate estimate: 1399915.6↓, 1190593.2↑ (6↓, 45↑ TCP MSS/op)
Test duration: 30 s.

4.内存占用如何 ?

quickws 的特色之一是低内存占用。

1w 连接的 tps 测试,1k payload 回写,初始内存占用约 122MB , 在 240s-260s 之后大约 86MB ,

1835 次点击
所在节点    Go 编程语言
22 条回复
eudore
286 天前
@guonaihong go get -v -u 已正常

一段 action 修改建议,使用 matrix 区分 arch ,另外 race 和 cover 可以一起测试,但是 covermode 有个值不支持。

```yaml
strategy:
max-parallel: 2
matrix:
go: [ '1.20']
arch: [amd64, 386]
name: Go-${{ matrix.go }} ${{ matrix.arch }}
env:
GOARCH: ${{ matrix.arch }}
steps:
- name: Run Tests
run: go test -v -timeout=1m -race -cover -coverprofile=coverage.out ./...
```
guonaihong
286 天前
@eudore 挺好的建议,可以直接 pr 。哈哈。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/969160

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX