V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
The Go Programming Language
http://golang.org/
Go Playground
Go Projects
Revel Web Framework
dw2693734d
V2EX  ›  Go 编程语言

golang 的哪个 websocket 好用?

  •  
  •   dw2693734d · 2023-02-25 16:48:54 +08:00 · 7438 次点击
    这是一个创建于 662 天前的主题,其中的信息可能已经有所发展或是发生改变。

    gorilla 已经不维护了

    第 1 条附言  ·  2023-02-25 21:50:04 +08:00
    这个不错,不用去管理什么 ping pong 之类的
    github.com/olahol/melody
    62 条回复    2023-05-17 17:56:53 +08:00
    0o0O0o0O0o
        1
    0o0O0o0O0o  
       2023-02-25 16:52:03 +08:00 via iPhone   ❤️ 2
    gws ,作者在本站比较活跃
    /t/908529
    /t/917122
    learningman
        2
    learningman  
       2023-02-25 16:55:35 +08:00
    lesismal
        3
    lesismal  
       2023-02-25 17:08:14 +08:00   ❤️ 6
    1. gorilla 虽然不维护了,但是生产被用了那么多,已经足够稳定,可以继续使用,从另一个角度讲,人家足够稳定没有必要继续维护了,凭什么就不能用了
    2. 基于 gorilla 的 melody 接口设计比较不错,推荐
    3. gorilla 虽然不错,但性能一般,而且需要类似 melody 那种做些额外的封装,比如广播场景需要单独的写协程、否则遇到单个 conn 阻塞可能导致其他 conn 被卡
    4. fasthttp/websocket 在 gorilla 基础上改造,性能也一般


    推荐下我自己的吧:
    https://github.com/lesismal/nbio

    1. 可以直接基于标准库 http 的框架之上使用、性能好于 gorilla ;
    2. 可以使用 nbio 自带的 poller 作为网络异步 io ,能够支持海量并发比如百万连接,而其他基于标准库阻塞 io 接口的框架连接数太多时协程数量太大,内存容易 oom ,gc 容易卡顿;
    3. 支持多种 io 模式,IOModMixed 模式下能够同步阻塞 io 与异步非阻塞 io 共用,新连接到来时根据当前在线量是否在设置的阈值以内,低于阈值使用标准库的同步阻塞 io 接口获得更高性能,高于阈值使用 nbio 的异步非阻塞 io 、协程数量最高占用配置的数量、动态退出,协程数量、内存各项占用均衡稳定
    4. 不需要额外封装读写协程,只需要设置好 OnOpen/OnClose/OnMessage/KeepaliveTime 等用户需要的参数,用户专心处理业务逻辑

    @0o0O0o0O0o @learningman 欢迎也来瞧瞧。gws 比较新、我也有提 issue 可以看下
    lesismal
        4
    lesismal  
       2023-02-25 17:10:29 +08:00
    nbio websocket 直接基于标准库 http 的例子以及多种 io 模式的说明:
    https://github.com/lesismal/nbio/releases/tag/v1.3.5

    旧帖发了那么久,一条回复都没有:
    https://www.v2ex.com/t/892703
    0o0O0o0O0o
        5
    0o0O0o0O0o  
       2023-02-25 17:17:40 +08:00 via iPhone   ❤️ 1
    @lesismal 本来想推荐你的,没记起来项目名字,他最近发了个对比帖所以记得他的
    lesismal
        6
    lesismal  
       2023-02-25 17:46:24 +08:00
    @0o0O0o0O0o
    哈哈哈,感谢关注支持!
    刚看到 melody 里有人提到因为 gorilla 不维护了、要不要切换到基于 nbio 或者其他基于框架,我也去凑热闹回复了下
    https://github.com/olahol/melody/issues/73
    DefoliationM
        7
    DefoliationM  
       2023-02-25 19:10:53 +08:00
    Nazz
        8
    Nazz  
       2023-02-25 19:30:20 +08:00
    @0o0O0o0O0o 谢谢老铁
    Nazz
        9
    Nazz  
       2023-02-25 19:38:20 +08:00
    自荐一下 gws, 性能在基于 net/http 的 websocket 实现里面可以说是一骑绝尘

    ### Features
    - 无依赖
    - 每个连接只会有一个常驻的协程
    - 支持异步非阻塞 Write; 支持单连接内并发处理多个请求, 控制好了并发上限
    - 默认模式下即可 100%通过 autobahn-testsuite websocket 协议测试

    https://github.com/lxzan/gws
    https://dev.to/lxzan/go-websocket-benchmark-31f4
    Nazz
        10
    Nazz  
       2023-02-25 19:59:56 +08:00 via Android
    @lesismal gorilla 比 nhooyr 和 gobwas 强多了,但性能和易用性不如 gws
    0o0O0o0O0o
        11
    0o0O0o0O0o  
       2023-02-25 20:05:09 +08:00 via iPhone
    @Nazz 感觉 nhooyr ws 的 net.Conn wrapper 挺好用的
    Nazz
        12
    Nazz  
       2023-02-25 20:14:56 +08:00 via Android   ❤️ 1
    @0o0O0o0O0o 可以看看我的 WebSocket Event ,借鉴自 JavaScript
    Nazz
        13
    Nazz  
       2023-02-25 20:18:38 +08:00 via Android
    @0o0O0o0O0o nhooyr API 挺精简的,但 rps, latency, cpu 均不如 gorilla
    webcape233
        14
    webcape233  
       2023-02-25 20:27:48 +08:00 via iPhone
    我最近公司项目,用的 go 自带的 ws,不清楚各种 ws 库主要差别在哪
    Nazz
        15
    Nazz  
       2023-02-25 20:47:20 +08:00 via Android
    @webcape233 golang.org/x 那个很挫
    Nazz
        16
    Nazz  
       2023-02-25 20:53:45 +08:00 via Android
    @webcape233 差别在于性能,feature, 以及对 websocket 协议的支持程度.
    c88155745
        17
    c88155745  
       2023-02-25 22:09:27 +08:00
    dw2693734d
        18
    dw2693734d  
    OP
       2023-02-25 22:11:51 +08:00
    @lesismal 大神也有维护 melody 啊,牛逼
    lesismal
        19
    lesismal  
       2023-02-25 23:10:17 +08:00   ❤️ 1
    @Nazz
    我这简单测了多个,tinyws ,nbio ,gws ,都高于 gorilla 的,三者数据差不多,连接数不是特别大的时候,我环境下 tinyws 略快一点点,nbio 、gws 差不多,nbio 的内存占用最少,连接数越大,nbio 会越均衡。没有做大量测试,比如不同 body size ,综合情况没做统计:
    https://github.com/lesismal/nbio-examples/tree/master/websocket_1m
    lesismal
        20
    lesismal  
       2023-02-25 23:19:53 +08:00
    @dw2693734d
    哈哈哈,没参与日常维护,就之前偶尔读了下 melody 源码,发现那有个并发的问题会低概率导致 panic ,肉眼看出来,写测试一模拟果然就复现了,然后 pr 了下,那时候正好是疫情严重时期,作者消失很久了,我都担心他会不会感染了严重了之类的,还好后来他又出现了,并且 merge 了我的 pr

    一些知名库的作者对并发这块的实现其实也不够好,一是被 go 官方和很多人鼓吹要用 chan 、少用锁,二是 go 这种多并发流确实复杂、肉眼难看出问题、测试压测也很难测出问题、甚至没办法模拟复现只能靠肉眼和想象力。。。

    最近一个比较火的 conc 库也扫了几眼,接口设计非常优雅美丽,但是代码的实现也是受到太多尽量用 chan 少用锁这种观点的影响,所以它的实现是性能差一些的,不过这点性能损失对通常的项目也没什么影响、也还好
    lesismal
        21
    lesismal  
       2023-02-25 23:23:32 +08:00
    @Nazz #9 gws 不是一骑绝尘的。。。你看我 #19 ,你把 tinyws 和 nbio 的 IOModBlocking 和 IOModMixed 也跑来对比下试试。。
    lesismal
        22
    lesismal  
       2023-02-25 23:27:40 +08:00
    nbio 的 websocket 解析器,是支持异步流数据解析的、同步 io 的数据丢进去也一样解析,所以本身是有点吃亏的、逻辑比同步解析复杂得多,而且涉及半包 cache 之类的,否则只是同步解析,还能实现得更快。同步和异步 io 都能处理,所以结合自身的 IOModMixed ,能把性能和硬件消耗做到最佳,不同在线量全 cover ,其他框架高在线量太吃硬件资源了
    lesismal
        23
    lesismal  
       2023-02-25 23:34:38 +08:00
    @guonaihong 好像很久没出现了,是在忙着搬砖嘛 :joy:
    Nazz
        24
    Nazz  
       2023-02-25 23:42:00 +08:00 via Android
    @lesismal 做测试很费时间,在测评里我只测了 1KiB ,
    lesismal
        25
    lesismal  
       2023-02-25 23:52:53 +08:00
    @Nazz 是啊,多个框架跑一轮还得统计很麻烦,而且可以测试得内容太多了,tls non-tls http ws rpc 各种,所以一些项我也只跑了简单测试
    guonaihong
        26
    guonaihong  
       2023-02-25 23:57:00 +08:00
    @lesismal 别提了,搬砖已经冒出火星子了。最近最大的痛点就是搬得不够快。副作用就撸了一个 https://github.com/antlabs/h2o ,日产千行代码不是梦。
    Nazz
        27
    Nazz  
       2023-02-26 00:08:26 +08:00 via Android
    @lesismal 用 Prometheus+Grafana 统计挺方便的,就
    Nazz
        28
    Nazz  
       2023-02-26 00:08:51 +08:00 via Android
    @Nazz 就怕机器不够强大
    lesismal
        29
    lesismal  
       2023-02-26 00:36:42 +08:00
    @guonaihong 我是超级不喜欢 grpc ,哈哈哈,用我自己的 arpc 一把梭才是爽。。。
    lesismal
        30
    lesismal  
       2023-02-26 00:40:30 +08:00
    @Nazz #27
    这有点杀鸡用牛刀了,而且还是要统计 qps/tps 结合进程自己的消耗,压测里自带这些工具就可以的,比如字节这种:
    github.com/cloudwego/kitex-benchmark

    但是字节这个 perf 不跨平台,我自己用 gopsutil 简单封装了些:github.com/lesismal/perf
    lesismal
        31
    lesismal  
       2023-02-26 01:51:39 +08:00
    @Nazz 我之前的简单压测是几个字节的 payload ,刚才又试了下 1k 字节 payload ,我环境得到的数据:
    1k-1w 范围连接数时,tinyws 和 gws qps 差不多,nbio 略快,内存占用 gws > tinyws > nbio ,但内存差距不算特别大
    10w 连接时,nbio 快更多一些,内存占用 gws 约 4.17G ,tinyws 约 3.73G, nbio 约 1.2G ,内存占用差距比较明显,而且 gws 和 tinyws 的 qps 不稳定、qps 摆动幅度较大,应该是连接数太多 gc 时 stw 更明显导致,nbio 的 qps 比较稳定
    相同连接数压测的 cpu 消耗差不多,nbio 好像略低
    Nazz
        32
    Nazz  
       2023-02-26 06:07:15 +08:00 via Android
    @lesismal 连接数太多 net/http 肯定干不过 gnet/nbio 这些实现的
    Nazz
        33
    Nazz  
       2023-02-26 08:42:57 +08:00
    @lesismal ws 客户端我是用的 tcpkali, cpu 占用率超低
    Nazz
        34
    Nazz  
       2023-02-26 08:55:17 +08:00
    @lesismal 刚测了一下 1000 连接, nbio IOModBlocking 跑了 2600Mbps, gws:dev 同步读异步写是 5800Mbps, nbio 兼容多种 IO 模式是有开销的吧
    Nazz
        35
    Nazz  
       2023-02-26 09:01:41 +08:00
    @lesismal Prometheus+Grafana 可视化效果好, 配置一次就可以了, 有钱的话真想搞一台线程撕裂者, 跑 k8s 来做测试.
    Nazz
        36
    Nazz  
       2023-02-26 10:02:14 +08:00 via Android
    @lesismal 很多人被带偏了,无脑选择 channel 。随便想想就知道,有锁的 channel 怎么可能比直接用锁高效
    lesismal
        37
    lesismal  
       2023-02-26 13:26:29 +08:00
    @Nazz
    > 刚测了一下 1000 连接, nbio IOModBlocking 跑了 2600Mbps, gws:dev 同步读异步写是 5800Mbps, nbio 兼容多种 IO 模式是有开销的吧

    代码发我下我试试看。多种 io Upgrade 后就没什么大影响了
    lesismal
        38
    lesismal  
       2023-02-26 13:27:58 +08:00
    @Nazz go 做简单 curd 确实不难,但是这种遍地并发对绝大多数人来说都挺难的,chan 适合解耦、串行化,能避免并发不熟的人一不小心就死锁了,但是性能肯定差一些
    lesismal
        39
    lesismal  
       2023-02-26 13:30:04 +08:00
    @Nazz 运维用这种监控集群健康挺好,做 benchmark ,尽量保持测试环境稳定、少部署额外软件好些
    Nazz
        40
    Nazz  
       2023-02-26 13:32:28 +08:00 via Android
    Nazz
        41
    Nazz  
       2023-02-26 13:33:34 +08:00 via Android
    @lesismal 对 channel 使用不熟悉也很容易死锁
    Nazz
        42
    Nazz  
       2023-02-26 14:41:07 +08:00
    @lesismal 用普罗米修斯监控确实影响了测试 RPS, 同样的命令, 去掉监控后跑到了 8000Mbps, 加上监控只有 4400Mbps.
    但是我那个测试项目里面, gws 和 nbio 运行条件都是一样的
    gws 同步读同步写: Aggregate bandwidth: 4384.217↓, 4381.833↑ Mbps
    nbio 阻塞模式: Aggregate bandwidth: 2558.850↓, 2471.334↑ Mbps
    命令: tcpkali -c 1000 --connect-rate 500 -r 2000 -T 3000s -f assets/1K.txt --ws 127.0.0.1:${port}/connect
    lesismal
        43
    lesismal  
       2023-02-26 15:26:12 +08:00
    @Nazz #42
    我刚跑了下 tcpkali ,按照你的参数确实 gws 更快,但是我本地得到的数据差异没那么大。
    netstat 查了下,tcp 收发缓冲区待读写的 size 较大、远超过单个包 size ,加上 tcpkali 的-r 参数你设置的 500 (每秒向每个连接发送 500 个包),这说明 tcpkali 压测方式是不管对方是否回包,都会按照-r 这个频率往对方发包。

    nbio 的解析器是异步流解析器,在-r 500 这种测试情况下,相比于 gws 的同步读同步解析,nbio 要处理更多的包边界、半包缓存之类的,所以会不划算。但实际场景中单个连接 client 向 server 每秒 500 个包是不太可能的、fps 类也远低于此。server 端推送也不会有这么高的频率。如果是有人来攻击 server 、这个连接响应慢了也不怕。

    1000 连接这种,如果把-r 调整到正常交互频率,性能就都差不多了。

    我又尝试把-r 20 、连接数提高到 2w ,nbio 的数据会更好些:
    tcpkali -c 20000 --connect-rate 5000 -r 20 -T 30s -f ./1K.txt --ws 127.0.0.1:28001/ws
    2w 连接 gws 遇到过一次不知道是不是触发了 bug ,2w 在线、协程数却达到了 3w+,tcpkali 30 秒竟然也没返回,不知道是不是 tcpkali 测试 bug 导致不断有新连接才导致 gws 协程 3w+,忘记了保存现场、后面多跑了几轮没再遇到,所以暂时没法分析这个了

    更大连接数用 tcpkali 不太好测了,多个节点一是卡、二是建立连接的时段不一样,有的节点建立连接后就开始猛发数据把 server 打满了,新节点再进入就困难了。但一些海量连接的业务的话可能是连接数多但频率没这么高所以新连接进来速度也还好。
    这也是为什么我之前没用第三方的工具进行百万链接的测试、而是自己写 client 来测试的原因:
    https://github.com/lesismal/nbio-examples/blob/master/websocket_1m/client/client.go

    tinyws 在-r 500 的情况下跟 nbio 差不多可能略慢,我没看 tinyws 的代码、暂时没有分析慢的原因
    lesismal
        44
    lesismal  
       2023-02-26 15:28:13 +08:00
    > 用普罗米修斯监控确实影响了测试 RPS, 同样的命令, 去掉监控后跑到了 8000Mbps, 加上监控只有 4400Mbps.

    @Nazz
    对,额外的重量级工具本身带来的消耗影响大,而且他们的消耗也不是平均持续稳定的、没法保障对不同框架测试时段的环境公平性,所以大家做压测通常不使用这些
    lesismal
        45
    lesismal  
       2023-02-26 15:37:21 +08:00
    @Nazz
    我能想到的高频发包的场景,比如 RPC ,但是 RPC 通常内网,TCP 更具优势(所以我非常抵制 GRPC ),而且 RPC 服务承载的 client 连接数并不会特别大,所以基于标准库同步方案、同步解析就 ok 了,我的 arpc 也是这样。但是 arpc 也支持 web 前端 client 直接通过 http/websocket 交互,这时候虽然可能连接数量也很大,但是这种用户并不是服务之间的 RPC 调用、所以也不会有服务之间 RPC 那种单个连接高频发包的场景、真有攻击的话它自己这个连接慢了也活该。。。

    我暂时没看懂 tcpkali 有没有办法进行类似 echo 测试这种,发-收-发-收,这样即使连接数不是特别大也可以把 server 性能跑满、更符合实际场景一些,而不是像上面那样通过-r 超过正常需求范围的情况下打满
    Nazz
        46
    Nazz  
       2023-02-26 16:11:37 +08:00 via Android
    @lesismal iops 较低的情况下,gorilla, gws, nbio 性能都够用了,上限越高说明库本身的开销越小
    Nazz
        47
    Nazz  
       2023-02-26 16:34:42 +08:00 via Android
    @lesismal 没有任何业务负载的情况下,gws 的性能上限是每秒收发各 100w+个 1KB 的 websocket frame ,可以看到上下行带宽是对等的
    lesismal
        48
    lesismal  
       2023-02-26 17:49:51 +08:00
    @Nazz #46
    不同的参数下,跑出来的数据是不一样的。 -r 500 这种,在正常的服务里限流机制直接就把它 close 了的。
    不同连接数、包体、 -r 取正常值范围的情况下,你可以试一下看看各个框架带宽和消耗能跑到多少,我环境下得到的数据并不是哪个固定第一,cpu 占用有差别,有的 cpu 高一些但是同样获得了更高的吞吐,有的 cpu 利用率上不去、跑多轮也还是低消耗低带宽比较奇怪。框架数量太多参数太多,跑几轮下来我眼睛都花了

    不能只以一种参数来确定性能
    Nazz
        49
    Nazz  
       2023-02-26 19:30:24 +08:00
    @lesismal 控制下变量就很好对比性能了, 同样的网络库, 连接数和 IOPS 下, 对比延迟分布, 延迟百分位和 CPU 内存占用. nbio 我最佩服的是手搓 tls, 那玩意一看就不简单.
    lesismal
        50
    lesismal  
       2023-02-26 19:52:48 +08:00   ❤️ 1
    @Nazz
    对,得多弄点不同变量参数对比,鸟窝老师 rpc 的测试里不同参数对比就相对全面一些

    tls 直接标准库 copy 一份魔改的,难度倒是不大,主要是琐碎,debug 、覆盖比较烦躁
    lesismal
        51
    lesismal  
       2023-02-26 20:00:11 +08:00
    @Nazz
    好些个开源的 IM 项目用的都是基于标准库同步 io 的 conn ,真要是在线量大,他们那个服务器节点数量成本会很高,如果换 nbio ,估计能省掉至少三分之二硬件成本,集群内部通信用的 GRPC 如果换成 arpc 走 tcp 、性能又能提高不少。
    nbio 最初的定位是解决 1m-connection ,几个月前还都不支持多 io 模式的,一般在线量的场景,纯 poller 的响应性能跟基于标准库的相比实在是没得比,现在平衡多了

    其实如果放到量大的业务上做基础设施,都能省很多成本。但如果真的量巨大,公司有钱,还是 rust 或者 c/cpp 好些。前阵子 cloudflare 说他们内部的 rust proxy 性能相当好

    go 版本的这些,跟 rust/c/cpp 的相比,实在是撵不上了,如果还年轻,我就去玩 rust 了,但是年纪不小,玩不动了
    lesismal
        52
    lesismal  
       2023-02-26 20:03:05 +08:00   ❤️ 1
    @Nazz #32
    我最早的时候是看到 evio ,国人又有人改进写了 gev 和 gnet ,想直接用他们的,但是功能太少了,只能用于 4 层,7 层自定义协议,tls/http/ws 这些统统没有,而且 gnet 性能略好于 gev ,但是没有实现 net.Conn ,想基于它封装也是非常麻烦,所以放弃了,自己撸着玩,慢慢就把功能加得越来越多了
    Nazz
        53
    Nazz  
       2023-02-26 21:19:59 +08:00 via Android
    @lesismal 我本来想学 rust 刷题玩玩,直接劝退了,写点数据结构太劳心了.
    Nazz
        54
    Nazz  
       2023-02-26 21:24:18 +08:00 via Android
    @lesismal 不看好 rust ,门槛决定了小众,底层开发才会考虑.
    lesismal
        55
    lesismal  
       2023-02-26 23:27:58 +08:00
    @Nazz c/cpp/rust 掌控力强,我就最喜欢 c void*,无拘无束,go 虽然写着省力写的快挺爽,但是找不到那种尽情优化做到极致控场的感觉,虽然爽但爽不到极致,只是均衡的感觉
    Nazz
        56
    Nazz  
       2023-02-26 23:42:30 +08:00 via Android   ❤️ 1
    @lesismal c/cpp 自由,rust 是被编译器教做人
    lesismal
        57
    lesismal  
       2023-02-27 14:59:30 +08:00
    @Nazz
    我是真有点想搞 rust 了:
    mp.weixin.qq.com/s/1dB4P54tVz-aM2iYIkE4fg
    Nazz
        58
    Nazz  
       2023-02-27 16:51:08 +08:00
    @lesismal 时间充裕就学起来吧
    pushy
        59
    pushy  
       2023-02-27 18:44:18 +08:00
    dw2693734d
        60
    dw2693734d  
    OP
       2023-03-02 12:28:59 +08:00
    @Nazz 被编译器教做人, 哈哈,笑死
    Nazz
        61
    Nazz  
       2023-03-02 17:20:52 +08:00
    @dw2693734d 自引用结构没法玩
    lesismal
        62
    lesismal  
       2023-05-17 17:56:53 +08:00
    @pushy gobwas/ws 存在明显缺陷,我有在它的 issue 列表里讨论,有兴趣可以翻看下。作者的意思是不想解决并且 close 了 issue (其实按 gobwas 的思路也确实没办法解决)
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   4785 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 34ms · UTC 03:58 · PVG 11:58 · LAX 19:58 · JFK 22:58
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.