Github 主页

📖 简介

gnet

这个项目存在的价值是提供一个在网络包处理方面能和 Redis、Haproxy 这两个项目具有相近性能的 Go 语言网络服务器框架。

gnetgnetgnet
gnetevio

🚀 功能

gnet

💡 核心设计

多线程/Go 程模型

主从多 Reactors 模型

gnetnetty

它的运行流程如下面的时序图:

主从多 Reactors + 线程/Go 程池

EventHandler.React
gnetEventHandler.Reactnetty
gnetEventHandler.React

模型的架构图如下所示:

它的运行流程如下面的时序图:

gnetants
gnetantspool.NewWorkerPoolantsEventHandler.Reactgnet.Conn.AsyncWrite
gnetants

自动扩容的 Ring-Buffer

gnet

🎉 开始使用

前提

gnet

安装

go get -u github.com/panjf2000/gnet
gnetimport "github.com/panjf2000/gnet"go [build|run|test]

使用示例

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

👏 贡献者

ants

🙏 致谢