修改 go websocket server 启动方式, 内存占用立省 40% !

2023-04-27 14:03:20 +08:00
 Nazz

最近发布了 gws v1.4.7 更新, 支持从 tcp conn 直接解析 websocket 协议, 降低内存占用. 大部分人使用 go websocket server, 都是复用的 http server, 这种劫持http连接升级的方式, 最大的弊端就是浪费内存. 由于 go http hijack 的缺陷, 一些内存一直得不到释放, 大概每个连接 10KB. 测试 10000 个连接的场景, 换用 Demo2 方式, 内存占用立省 42.86%!

Demo 1: hijack

package main

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

func main() {
	upgrader := gws.NewUpgrader(new(gws.BuiltinEventHandler), nil)

	http.HandleFunc("/connect", func(writer http.ResponseWriter, request *http.Request) {
		socket, err := upgrader.Accept(writer, request)
		if err != nil {
			log.Printf(err.Error())
			return
		}
		go socket.Listen()
	})

	if err := http.ListenAndServe(":3000", nil); err != nil {
		log.Fatalf(err.Error())
	}
}

Demo 2: direct

package main

import (
	"github.com/lxzan/gws"
	"log"
)

func main() {
	srv := gws.NewServer(new(gws.BuiltinEventHandler), nil)

	if err := srv.Run(":3001"); err != nil {
		log.Panicln(err.Error())
	}
}

2094 次点击
所在节点    Go 编程语言
38 条回复
lesismal
2023-04-27 21:12:46 +08:00
@Nazz #19 就是因为这些各种原因,很多地方我手撸 buffer ,好累:joy: 。。。
Nazz
2023-04-27 21:26:01 +08:00
@lesismal 抠细节心累
Nazz
2023-04-27 21:27:11 +08:00
@lesismal bytes.Buffer 应该比手撸的更快
lesismal
2023-04-27 22:08:40 +08:00
> bytes.Buffer 应该比手撸的更快

并不是。首先它一样的是挂载在 conn 上需要持续占用大段 buffer ,其次它源码你看下就知道了,还是那些个 buffer 的操作、只是封装些常用方法方便用户罢了。buffer 操作这种事情并没有什么性能提升的秘诀,就是谁的逻辑代码消耗更少、拷贝更少之类的
Nazz
2023-04-27 22:24:10 +08:00
@lesismal 我记得即使把 bytes.Buffer 源码复制出来,IO 速度也会变慢
lesismal
2023-04-27 22:38:11 +08:00
@Nazz 得实测看看,正常情况狂不应该有这个问题。差异不大可能是测试稳定性的问题可以忽略。如果差异很大,代码发我瞧瞧
Nazz
2023-04-27 22:52:06 +08:00
@lesismal 明天再看看吧
Nazz
2023-04-28 10:26:54 +08:00
@lesismal 现在改成用户态拼接 buffer 的方式了, bytes.Buffer 没有 Discard 方法, 压缩那块写得有点丑
lysS
2023-04-28 10:29:29 +08:00
@lesismal 浏览器的 ws ,默认都是先发个 http 头,然后服务器 Upgrade 吗?
Nazz
2023-04-28 10:31:39 +08:00
@lysS request header 和 http/1.1 是一模一样的
Nazz
2023-04-28 13:39:56 +08:00
@Nazz 忽然发现 next 就相当于 Discard :)
lesismal
2023-04-28 14:56:56 +08:00
@lysS 跟是不是浏览器应该没关系,而是 ws 协议就是这么规定的,go 的 client 也是要发这个握手的 request 的。
lesismal
2023-04-28 15:00:45 +08:00
> 但毕竟不是 c free ,不能立即释放

#4 更正,c free 也不一定是立即归还的、而且多数时候不是立即归还,要看分配器实际情况了

> 单就 upgrade 这个 request 而言,可能上下文的代价比新 go 更大一点

#15 更正,单就 upgrade 这个 request 而言,可能相比于上下文的代价、新 go 代价更大一点
lesismal
2023-04-28 15:05:10 +08:00
> 现在改成用户态拼接 buffer 的方式了, bytes.Buffer 没有 Discard 方法, 压缩那块写得有点丑
> 忽然发现 next 就相当于 Discard :)

@Nazz 主要是你 write frame 的场景,就一 head+body 拼接,太简单了,pool+append 足矣
Nazz
2023-04-28 16:00:12 +08:00
@lesismal copy 比 append 更快些
Nazz
2023-04-28 16:12:48 +08:00
@lesismal 不开新 goroutine 好点, 反正 http 包里面很多东西 gc 不了.
lesismal
2023-04-28 18:24:08 +08:00
> copy 比 append 更快些

恩。
前提是得比较明确 size cap 这些,很多地方是 buffer size 可能不够,即使 copy 也是得先 append 扩容。
预先知道 size 并且分配了足够 size 的 buffer ,我也是 copy:
https://github.com/lesismal/nbio/blob/master/nbhttp/websocket/conn.go#L263
lesismal
2023-04-28 18:25:37 +08:00
> 不开新 goroutine 好点, 反正 http 包里面很多东西 gc 不了.

是。标准库让人又爱又恨的

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

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

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

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

© 2021 V2EX