项目地址:
陈年旧帖:
因为标准库的 UDP 足够好用,所以之前一直没有支持 UDP ,但偶尔还是会有人问 UDP 相关,所以就干脆支持上了,并且相比于标准库和其他 Poller 框架有一点差别、nbio 的 UDP 更方便一些:
其实本来也是支持的,但需要用户自己进行 Listen ,然后通过 nbio.Engine.AddConn 来实现,现在支持了 NewEngine 时配置 unix 地址的方式,更方便。
这个是近期较大的更新,主要支持了三种 IO 模式。
这种支持,缘起自去年鸟窝老师的 2021 年 Go 生态圈 rpc 框架 benchmark 中的讨论,当时有想到这种阻塞与非阻塞 IO 混合使用的方案,以此来提高常规在线量时的相应性能,承载高在线时保持低占用、较好的响应性能和稳定性。
Poller 框架,IO 与逻辑协程池是分离的,相比于标准库的每个连接至少一个协程循环读取的方案存在较大的差别: | 方案 | 标准库 | Poller | | --------------- | -------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------- | | 协程数量 | 每个连接至少一个协程 | 数量可控的 IO 协程池与逻辑协程池处理所有连接 | | 协议解析 /编解码 | 同步读取和解析,逻辑更简单,不需要考虑半包的缓存与拼接等 | 异步解析器,逻辑复杂,需要处理半包缓存与拼接 | | buffer 优化 | 同一个协程内循环,one-by-one 读取和解析,buffer 自然复用,如果消息体不大,buffer 为栈内存 | IO 与逻辑协程分离,跨协程 buffer 生命周期管理、复用难度大 | | 内存占用 | 每个连接至少一个协程,高在线量时内存占用高 | 协程数量可控,可以做到 Conn 无请求时不持有 buffer ,内存占用低 | | GC/STW | 高在线量时协程数量变量数量内存占用三高,GC 开销大,容易 STW/OOM | 协程、变量、buffer 数量均可控,GC 、内存都比较稳定,能承载更高在线数 | | 性能 | 在线量不高时响应性能更好,高在线则不稳定 | 简单测试中,在线量不高时响应性能可能不比标准库方案好,实际 IO 场景可能好于标准库;高在线时仍能稳定响应、优于标准库 |
支持了三种模式后: | IOMod | 说明 | | ---------------- | :--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | | IOModNonBlocking | 与原来的纯 Poller 方案没什么区别 | | IOModBlocking | 阻塞模式下,所有连接使用至少一个协程来处理读,Websocket 提供了配置项来开启第二个协程处理写、以此避免广播场景下单个连接写阻塞导致其他连接的等待 | | IOModMixed | 设置了这种模式,还可以通过 Engine.MaxBlockingOnline 来设置阻塞 IO 的连接数量阈值,如果当前阻塞模式处理的连接数量小于这个阈值,则新连接也由阻塞模式处理,否则由 Poller 异步 IO 的方式处理 |
IOModBlocking
的优势是低 /普通在线场景获得更好的响应性能,我在简单压测中,nbio 的 http1.x 好于标准库,websocket 略好于 gorilla/websocket 。
IOModMixed
的目标是在不同场景、服务活跃程度和负载期间获取更好的性能与资源消耗的平衡,能够承载更大在线量并保持地占用和稳定性,在线数不高时响应性能更高。
另外,Websocket 也支持了标准库 http server ,使用方式也简单,例子如下:
package main
import (
"fmt"
"log"
"net/http"
"github.com/lesismal/nbio/nbhttp/websocket"
)
func echo(w http.ResponseWriter, r *http.Request) {
u := websocket.NewUpgrader()
u.OnMessage(func(c *websocket.Conn, mt websocket.MessageType, data []byte) {
c.WriteMessage(mt, data)
})
_, err := u.Upgrade(w, r, nil)
if err != nil {
log.Print("upgrade:", err)
return
}
}
func main() {
mux := &http.ServeMux{}
mux.HandleFunc("/ws", echo)
server := http.Server{
Addr: "localhost:8080",
Handler: mux,
}
fmt.Println("server exit:", server.ListenAndServe())
}
The websocket
now also support std http server, and I got a better performance and lower mem/cpu cost than std in a simple load test. The example of using with std http server would be like this:
package main
import (
"fmt"
"log"
"net/http"
"github.com/lesismal/nbio/nbhttp/websocket"
)
func echo(w http.ResponseWriter, r *http.Request) {
u := websocket.NewUpgrader()
u.OnMessage(func(c *websocket.Conn, mt websocket.MessageType, data []byte) {
c.WriteMessage(mt, data)
})
_, err := u.Upgrade(w, r, nil)
if err != nil {
log.Print("upgrade:", err)
return
}
}
func main() {
mux := &http.ServeMux{}
mux.HandleFunc("/ws", echo)
server := http.Server{
Addr: "localhost:8080",
Handler: mux,
}
fmt.Println("server exit:", server.ListenAndServe())
}
1
blackvv666 2023-04-18 01:20:02 +08:00 1
牛!!看源码学习
|
2
lesismal OP @blackvv666 感谢支持,欢迎交流,有建议、疑问、bug 之类的随时来 issue
|