Github 主页 博客原文

欢迎大家围观~~,目前还在持续更新,感兴趣的话可以 star 一下暗中观察哦。

简介
gnet

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

gnetgnetgnet
gnetevio
功能
  • 高性能 的基于多线程模型的 Event-Loop 事件驱动
  • 内置 Round-Robin 轮询负载均衡算法
  • 简洁的 APIs
  • 基于 Ring-Buffer 的高效内存利用
  • 支持多种网络协议:TCP、UDP、Unix Sockets
  • 支持两种事件驱动机制:Linux 里的 epoll 以及 FreeBSD 里的 kqueue
  • 支持异步写操作
  • 允许多个网络监听地址绑定在一个 Event-Loop 上
  • 灵活的事件定时器
  • SO_REUSEPORT 端口重用
核心设计

多线程/Go程模型

主从多 Reactors 模型

gnetnetty

[站外图片上传中...(image-fa8393-1569676539479)]

它的运行流程如下面的时序图:
[站外图片上传中...(image-b373cd-1569676539479)]

主从多 Reactors + 线程/Go程池

Event.React()
gnetEvent.React()netty
gnetEvent.React()

这个模型还在持续开发中并且很快就能完成,模型的架构图如下所示:

[站外图片上传中...(image-3a3d74-1569676539479)]

它的运行流程如下面的时序图:
[站外图片上传中...(image-b1778e-1569676539479)]

不过,在这个新的网络模型开发完成之前,你依然可以通过一些其他的外部开源 goroutine pool 来处理你的阻塞业务逻辑,在这里我推荐个人开发的一个开源 goroutine pool:ants,它是一个基于 Go 开发的高性能的 goroutine pool ,实现了对大规模 goroutine 的调度管理、goroutine 复用。

gnetantsants

通信机制

gnetgnetgnet

所以我最终选择了 go-disruptor:高性能消息分发队列 LMAX Disruptor 的 Golang 实现。

自动扩容的 Ring-Buffer

gnet

[站外图片上传中...(image-873474-1569676539479)]

开始使用

安装

$ go get -u github.com/panjf2000/gnet

使用示例

gnetgnet.Eventsgnet.Servegnet.ActionCosedShutdown
gnet

不带阻塞逻辑的 echo 服务器

package main

import (
    "log"
    "strings"

    "github.com/panjf2000/gnet"
)

func main() {
    var trace bool
    var events gnet.Events
    events.React = func(c gnet.Conn) (out []byte, action gnet.Action) {
        top, tail := c.ReadPair()
        out = append(top, tail...)
        c.ResetBuffer()
        if trace {
            log.Printf("%s", strings.TrimSpace(string(top)+string(tail)))
        }
        return
    }
    log.Fatal(gnet.Serve(events, "tcp://:9000", gnet.WithMulticore(true)))
}
gnetReactoutgnet

带阻塞逻辑的 echo 服务器

package main

import (
    "log"
    "time"

    "github.com/panjf2000/ants"
    "github.com/panjf2000/gnet"
)

func main() {
    var events gnet.Events

    // Create a goroutine pool.
    poolSize := 256 * 1024
    pool, _ := ants.NewPool(poolSize, ants.WithNonblocking(true))
    defer pool.Release()

    events.React = func(c gnet.Conn) (out []byte, action gnet.Action) {
        data := c.ReadBytes()
        c.ResetBuffer()
        action = DataRead
        // Use ants pool to unblock the event-loop.
        _ = pool.Submit(func() {
            time.Sleep(1 * time.Second)
            c.AsyncWrite(data)
        })
        return
    }
    log.Fatal(gnet.Serve(events, "tcp://:9000", gnet.WithMulticore(true)))
}

正如我在『主从多 Reactors + 线程/Go程池』那一节所说的那样,如果你的业务逻辑里包含阻塞代码,那么你应该把这些阻塞代码变成非阻塞的,比如通过把这部分代码通过 goroutine 去运行,但是要注意一点,如果你的服务器处理的流量足够的大,那么这种做法将会导致创建大量的 goroutines 极大地消耗系统资源,所以我一般建议你用 goroutine pool 来做 goroutines 的复用和管理,以及节省系统资源。

I/O 事件

gnet
OnInitCompleteOnOpenedOnClosedReactTickPreWrite

定时器

Tickdelay
gnet.Serving
events.Tick = func() (delay time.Duration, action Action){
    log.Printf("tick")
    delay = time.Second
    return
}

UDP 支持

gnetgnet.Servegnet
OnOpenedOnClosedReact

使用多核

gnet.WithMulticore(true)gnettrue

负载均衡

gnet

SO_REUSEPORT 端口复用

服务器支持 SO_REUSEPORT 端口复用特性,允许多个 sockets 监听同一个端口,然后内核会帮你做好负载均衡,每次只唤醒一个 socket 来处理 accept 请求,避免惊群效应。

reuseport=true
gnet.Serve(events, "tcp://:9000", gnet.WithMulticore(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
相关文章 待做事项

gnet 还在持续开发的过程中,所以这个仓库的代码和文档会一直持续更新,如果你对 gnet 感兴趣的话,欢迎给这个开源库贡献你的代码,还有你要是喜欢 gnet 的话,可以给个星星鼓励一下哦 ~~