gnet
这个项目存在的价值是提供一个在网络包处理方面能和 Redis、Haproxy 这两个项目具有相近性能的 Go 语言网络服务器框架。
gnetgnetgnet
gnetevio
功能
- 高性能 的基于多线程/Go程模型的 event-loop 事件驱动
- 内置 Round-Robin 轮询负载均衡算法
- 内置 goroutine 池,由开源库 ants 提供支持
- 内置 bytes 内存池,由开源库 pool 提供支持
- 简洁的 APIs
- 基于 Ring-Buffer 的高效内存利用
- 支持多种网络协议:TCP、UDP、Unix Sockets
- 支持两种事件驱动机制:Linux 里的 epoll 以及 FreeBSD 里的 kqueue
- 支持异步写操作
- 灵活的事件定时器
- SO_REUSEPORT 端口重用
核心设计
多线程/Go程模型
主从多 Reactors 模型
gnetnetty
它的运行流程如下面的时序图:
主从多 Reactors + 线程/Go程池
EventHandler.React
gnetEventHandler.Reactnetty
gnetEventHandler.React
模型的架构图如下所示:
它的运行流程如下面的时序图:
gnetants
gnetantspool.NewWorkerPoolantsEventHandler.Reactgnet.Conn.AsyncWrite
gnetants
自动扩容的 Ring-Buffer
gnet
开始使用
安装
$ go get -u github.com/panjf2000/gnet
使用示例
gnet
gnetgnet.EventHandlergnet.Servegnet.ActionCosedShutdown
gnet
不带阻塞逻辑的 echo 服务器
package main
import (
"log"
"github.com/panjf2000/gnet"
)
type echoServer struct {
*gnet.EventServer
}
func (es *echoServer) React(c gnet.Conn) (out []byte, action gnet.Action) {
out = c.Read()
c.ResetBuffer()
return
}
func main() {
echo := new(echoServer)
log.Fatal(gnet.Serve(echo, "tcp://:9000", gnet.WithMulticore(true)))
}
gnetEventHandler.Reactoutgnet
带阻塞逻辑的 echo 服务器
package main
import (
"log"
"time"
"github.com/panjf2000/gnet"
"github.com/panjf2000/gnet/pool"
)
type echoServer struct {
*gnet.EventServer
pool *pool.WorkerPool
}
func (es *echoServer) React(c gnet.Conn) (out []byte, action gnet.Action) {
data := append([]byte{}, c.Read()...)
c.ResetBuffer()
// Use ants pool to unblock the event-loop.
_ = es.pool.Submit(func() {
time.Sleep(1 * time.Second)
c.AsyncWrite(data)
})
return
}
func main() {
p := pool.NewWorkerPool()
defer p.Release()
echo := &echoServer{pool: p}
log.Fatal(gnet.Serve(echo, "tcp://:9000", gnet.WithMulticore(true)))
}
正如我在『主从多 Reactors + 线程/Go程池』那一节所说的那样,如果你的业务逻辑里包含阻塞代码,那么你应该把这些阻塞代码变成非阻塞的,比如通过把这部分代码通过 goroutine 去运行,但是要注意一点,如果你的服务器处理的流量足够的大,那么这种做法将会导致创建大量的 goroutines 极大地消耗系统资源,所以我一般建议你用 goroutine pool 来做 goroutines 的复用和管理,以及节省系统资源。
更多的例子可以在这里查看: gnet 示例。
I/O 事件
gnet
EventHandler.OnInitCompleteEventHandler.OnOpenedEventHandler.OnClosedEventHandler.ReactEventHandler.TickEventHandler.PreWrite
定时器
EventHandler.Tickdelay
gnet.ServingWithTicker(true)
events.Tick = func() (delay time.Duration, action Action){
log.Printf("tick")
delay = time.Second
return
}
UDP 支持
gnetgnet.Servegnet
EventHandler.OnOpenedEventHandler.OnClosedReact
使用多核
gnet.WithMulticore(true)gnettrue
负载均衡
gnet
SO_REUSEPORT 端口复用
服务器支持 SO_REUSEPORT 端口复用特性,允许多个 sockets 监听同一个端口,然后内核会帮你做好负载均衡,每次只唤醒一个 socket 来处理 accept 请求,避免惊群效应。
开启这个功能也很简单,使用 functional options 设置一下即可:
gnet.Serve(events, "tcp://:9000", gnet.WithMulticore(true), gnet.WithReusePort(true)))
性能测试
同类型的网络库性能对比
Linux (epoll)
系统参数
# Machine information
OS : Ubuntu 18.04/x86_64
CPU : 8 Virtual CPUs
Memory : 16.0 GiB
# Go version and configurations
Go Version : go1.12.9 linux/amd64
GOMAXPROCS=8
Echo Server
HTTP Server
FreeBSD (kqueue)
系统参数
# Machine information
OS : macOS Mojave 10.14.6/x86_64
CPU : 4 CPUs
Memory : 8.0 GiB
# Go version and configurations
Go Version : go version go1.12.9 darwin/amd64
GOMAXPROCS=4
Echo Server
HTTP Server
证书
gnet