基于 go-netty 编写的一款 websocket 库 nettyws

2023-11-03 09:18:05 +08:00
 limpo

nettyws是一款基于go-netty编写的高性能 websocket 库,底层协议编解码基于gobwas/ws但是针对性进行了额外的性能优化,相比较直接使用gobwas/ws库,nettyws的接口更加易于使用,性能也明显优于它

仓库地址: https://github.com/go-netty/go-netty-ws

API 预览

type Websocket
    func NewWebsocket(options ...Option) *Websocket
    func (ws *Websocket) Close() error
    func (ws *Websocket) Listen(addr string) error
    func (ws *Websocket) Open(addr string) error
    func (ws *Websocket) UpgradeHTTP(w http.ResponseWriter, r *http.Request) (Conn, error)

type Option
    func WithAsyncWrite(writeQueueSize int, writeForever bool) Option
    func WithBinary() Option
    func WithBufferSize(readBufferSize, writeBufferSize int) Option
    func WithCompress(compressLevel int, compressThreshold int64) Option
    func WithMaxFrameSize(maxFrameSize int64) Option
    func WithNoDelay(noDelay bool) Option
    func WithServeMux(serveMux *http.ServeMux) Option
    func WithServeTLS(tls *tls.Config) Option
    func WithValidUTF8() Option

性能报告

Framework TPS Conns Concurrency Payload CPU Avg CPU Max MEM Min MEM Avg MEM Max
gobwas 510595 10000 10000 1024 762.87 785.80 361.89M 364.79M 366.24M
nettyws 637288 10000 10000 1024 636.00 643.86 173.52M 178.02M 182.52M

详细性能压测数据来源: go-websocket-benchmark

如何使用

服务器

// create websocket instance
var ws = nettyws.NewWebsocket()

// setup OnOpen handler
ws.OnOpen = func(conn nettyws.Conn) {
    fmt.Println("OnOpen: ", conn.RemoteAddr())
}

// setup OnData handler
ws.OnData = func(conn nettyws.Conn, data []byte) {
    fmt.Println("OnData: ", conn.RemoteAddr(), ", message: ", string(data))
    conn.Write(data)
}

// setup OnClose handler
ws.OnClose = func(conn nettyws.Conn, err error) {
    fmt.Println("OnClose: ", conn.RemoteAddr(), ", error: ", err)
}

fmt.Println("listening websocket connections ....")

// listen websocket server
if err := ws.Listen("ws://127.0.0.1:9527/ws"); nil != err {
    panic(err)
}

客户端

// create websocket instance
var ws = nettyws.NewWebsocket()

// setup OnOpen handler
ws.OnOpen = func(conn nettyws.Conn) {
    fmt.Println("OnOpen: ", conn.RemoteAddr())
    conn.Write([]byte("hello world"))
}

// setup OnData handler
ws.OnData = func(conn nettyws.Conn, data []byte) {
    fmt.Println("OnData: ", conn.RemoteAddr(), ", message: ", string(data))
}

// setup OnClose handler
ws.OnClose = func(conn nettyws.Conn, err error) {
    fmt.Println("OnClose: ", conn.RemoteAddr(), ", error: ", err)
}

fmt.Println("open websocket connection ...")

// connect to websocket server
if err := ws.Open("ws://127.0.0.1:9527/ws"); nil != err {
    panic(err)
}

从标准 http 服务器升级

// create websocket instance
var ws = nettyws.NewWebsocket()

// setup OnOpen handler
ws.OnOpen = func(conn nettyws.Conn) {
    fmt.Println("OnOpen: ", conn.RemoteAddr())
}

// setup OnData handler
ws.OnData = func(conn nettyws.Conn, data []byte) {
    fmt.Println("OnData: ", conn.RemoteAddr(), ", message: ", string(data))
    conn.Write(data)
}

// setup OnClose handler
ws.OnClose = func(conn nettyws.Conn, err error) {
    fmt.Println("OnClose: ", conn.RemoteAddr(), ", error: ", err)
}

fmt.Println("upgrade websocket connections ....")

// upgrade websocket connection from http server
serveMux := http.NewServeMux()
serveMux.HandleFunc("/ws", func(writer http.ResponseWriter, request *http.Request) {
    ws.UpgradeHTTP(writer, request)
})

// listen http server
if err := http.ListenAndServe(":9527", serveMux); nil != err {
    panic(err)
}

特别说明,nettyws 不支持混合数据数据模式,也就是说服务器启动时必须指定数据包格式(文本/二进制,默认文本格式)如果收到了非指定数据包格式则将会被丢弃,这样做的原因之一是nettyws使用的底层传输层抽象成 io.ReadWriter 之后无法携带除数据之外的格式信息,同时真实业务场景上同时混用数据格式的情况比比较少见,因此直接放弃了混合数据格式的支持。

1526 次点击
所在节点    Go 编程语言
11 条回复
cian
2023-11-03 09:43:28 +08:00
不错已星
limpo
2023-11-03 09:50:26 +08:00
@cian 感谢 ❤
lifespy
2023-11-03 10:21:40 +08:00
看着好像很不错。我刚刚接触 go 不久,想问问,我举个例子。
如果说我想和 goframe 框架结合使用,这个路由该如何定义呢
limpo
2023-11-03 10:31:21 +08:00
@lifespy 抱歉 我对 goframe 不是太熟悉,一般基于标准库 server 的路由都可以参考 **从标准 http 服务器升级** 这个例子进行适配
lifespy
2023-11-03 13:54:50 +08:00
@limpo #4 试了下,可以的。都是标准 http 接口
https://img.cdn.xiubbs.com/file/23d18395dbb4d7d24c7cc.png
limpo
2023-11-03 14:08:37 +08:00
@lifespy 👍
yrzs
2023-11-03 16:48:39 +08:00
starred
limpo
2023-11-03 17:38:20 +08:00
@yrzs 感谢 ❤
lifespy
2023-11-06 11:09:00 +08:00
前端 js 发送文本消息,服务端接受时提示
OnClose: 127.0.0.1:52295 , error: flate: corrupt input before offset 6
但是用 apipost 模拟又没有问题
limpo
2023-11-06 15:42:34 +08:00
@lifespy 是和浏览器压缩协商出了点问题,可以更新一下版本到 v1.0.2
lifespy
2023-11-06 17:09:22 +08:00
@limpo #10 确实好了,这一些让我不敢用在项目里面了😅

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

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

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

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

© 2021 V2EX