gnet 是一个基于事件驱动的高性能和轻量级网络框架。它直接使用 epoll 和 kqueue 系统调用而非标准 Golang 网络包:net 来构建网络应用,它的工作原理类似两个开源的网络库:netty 和 libuv。

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

gnet 的亮点在于它是一个高性能、轻量级、非阻塞的纯 Go 实现的传输层(TCP/UDP/Unix-Socket)网络框架,开发者可以使用 gnet 来实现自己的应用层网络协议,从而构建出自己的应用层网络应用:比如在 gnet 上实现 HTTP 协议就可以创建出一个 HTTP 服务器 或者 Web 开发框架,实现 Redis 协议就可以创建出自己的 Redis 服务器等等。

gnet 衍生自另一个项目:evio,但性能远胜之。

功能

高性能 的基于多线程/Go程模型的 event-loop 事件驱动

内置 Round-Robin 轮询负载均衡算法

内置 goroutine 池,由开源库 ants 提供支持

内置 bytes 内存池,由开源库 pool 提供支持

简洁的 APIs

基于 Ring-Buffer 的高效内存利用

支持多种网络协议:TCP、UDP、Unix Sockets

支持两种事件驱动机制:Linux 里的 epoll 以及 FreeBSD 里的 kqueue

支持异步写操作

灵活的事件定时器

SO_REUSEPORT 端口重用

核心设计

多线程/Go程模型

主从多 Reactors 模型

gnet 重新设计开发了一个新内置的多线程/Go程模型:『主从多 Reactors』,这也是 netty 默认的线程模型,下面是这个模型的原理图:

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

主从多 Reactors + 线程/Go程池

你可能会问一个问题:如果我的业务逻辑是阻塞的,那么在 EventHandler.React 注册方法里的逻辑也会阻塞,从而导致阻塞 event-loop 线程,这时候怎么办?

正如你所知,基于 gnet 编写你的网络服务器有一条最重要的原则:永远不能让你业务逻辑(一般写在 EventHandler.React 里)阻塞 event-loop 线程,否则的话将会极大地降低服务器的吞吐量,这也是 netty 的一条最重要的原则。

我的回答是,基于gnet 的另一种多线程/Go程模型:『带线程/Go程池的主从多 Reactors』可以解决阻塞问题,这个新网络模型通过引入一个 worker pool 来解决业务逻辑阻塞的问题:它会在启动的时候初始化一个 worker pool,然后在把 EventHandler.React里面的阻塞代码放到 worker pool 里执行,从而避免阻塞 event-loop 线程,

模型的架构图如下所示:

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

gnet 通过利用 ants goroutine 池(一个基于 Go 开发的高性能的 goroutine 池 ,实现了对大规模 goroutines 的调度管理、goroutines 复用)来实现『主从多 Reactors + 线程/Go程池』网络模型。关于 ants 的全部功能和使用,可以在 ants 文档 里找到。

gnet 内部集成了 ants 以及提供了 pool.NewWorkerPool 方法来初始化一个 ants goroutine 池,然后你可以把 EventHandler.React 中阻塞的业务逻辑提交到 goroutine 池里执行,最后在 goroutine 池里的代码调用 gnet.Conn.AsyncWrite 方法把处理完阻塞逻辑之后得到的输出数据异步写回客户端,这样就可以避免阻塞 event-loop 线程。

有关在 gnet 里使用 ants goroutine 池的细节可以到这里进一步了解。

自动扩容的 Ring-Buffer

gnet 利用 Ring-Buffer 来缓冲网络数据以及管理内存。

开始使用

安装

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

使用示例

详细的文档在这里: gnet 接口文档,不过下面我们先来了解下使用 gnet 的简略方法。

用 gnet 来构建网络服务器是非常简单的,只需要实现 gnet.EventHandler接口然后把你关心的事件函数注册到里面,最后把它连同监听地址一起传递给 gnet.Serve 函数就完成了。在服务器开始工作之后,每一条到来的网络连接会在各个事件之间传递,如果你想在某个事件中关闭某条连接或者关掉整个服务器的话,直接把 gnet.Action 设置成 Cosed 或者 Shutdown就行了。

Echo 服务器是一种最简单网络服务器,把它作为 gnet 的入门例子在再合适不过了,下面是一个最简单的 echo server,它监听了 9000 端口:

不带阻塞逻辑的 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)))

}

正如你所见,上面的例子里 gnet 实例只注册了一个 EventHandler.React 事件。一般来说,主要的业务逻辑代码会写在这个事件方法里,这个方法会在服务器接收到客户端写过来的数据之时被调用,然后处理输入数据(这里只是把数据 echo 回去)并且在处理完之后把需要输出的数据赋值给 out 变量然后返回,之后你就不用管了,gnet 会帮你把数据写回客户端的。

带阻塞逻辑的 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 目前支持的 I/O 事件如下:

EventHandler.OnInitComplete 当 server 初始化完成之后调用。

EventHandler.OnOpened 当连接被打开的时候调用。

EventHandler.OnClosed 当连接被关闭的时候调用。

EventHandler.React 当 server 端接收到从 client 端发送来的数据的时候调用。(你的核心业务代码一般是写在这个方法里)

EventHandler.Tick 服务器启动的时候会调用一次,之后就以给定的时间间隔定时调用一次,是一个定时器方法。

EventHandler.PreWrite 预先写数据方法,在 server 端写数据回 client 端之前调用。

定时器

EventHandler.Tick 会每隔一段时间触发一次,间隔时间你可以自己控制,设定返回的 delay 变量就行。

定时器的第一次触发是在 gnet.Serving 事件之后,如果你要设置定时器,别忘了设置 option 选项:WithTicker(true)。

events.Tick = func() (delay time.Duration, action Action){

log.Printf("tick")

delay = time.Second

return

}

UDP 支持

gnet 支持 UDP 协议,在 gnet.Serve 里绑定 UDP 地址即可,gnet 的 UDP 支持有如下的特性:

数据进入服务器之后立刻写回客户端,不做缓存。

EventHandler.OnOpened 和 EventHandler.OnClosed 这两个事件在 UDP 下不可用,唯一可用的事件是 React。

使用多核

gnet.WithMulticore(true) 参数指定了 gnet 是否会使用多核来进行服务,如果是 true 的话就会使用多核,否则就是单核运行,利用的核心数一般是机器的 CPU 数量。

负载均衡

gnet 目前内置的负载均衡算法是轮询调度 Round-Robin,暂时不支持自定制。

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 的源码允许用户在遵循 MIT 开源证书 规则的前提下使用。

致谢

相关文章

linux 开源网卡驱动,【开源】gnet: 一个轻量级且高性能、基于事件驱动的 Go 网络库...相关推荐

  1. libgo高性能网络服务器,【开源】gnet: 一个轻量级且高性能的 Golang 网络库

    ![](https://ask.qcloudimg.com/http-save/1303222/sipe2g9n9h.png) # Github 主页 [https://github.com/panj ...

  2. 【开源推荐】gnet: 一个轻量级且高性能的 Go 网络库

    Github 主页 https://github.com/panjf2000/gnet 欢迎大家围观~~,目前还在持续更新,感兴趣的话可以 star 一下暗中观察哦. 简介 gnet 是一个基于事件驱 ...

  3. gnet: 一个轻量级且高性能的 Go 网络框架

    GitHub 主页 https://github.com/panjf2000/gnet 欢迎大家围观 ~~,目前还在持续更新,感兴趣的话可以 star 一下暗中观察哦.

  4. gnet: 一个轻量级且高性能的 Golang 网络库 置顶!

    https://taohuawu.club/go-event-loop-networking-library-gnet

  5. Linux服务器网卡驱动安装及故障排除(转)

    Linux服务器网卡驱动安装及故障排除(转) 转自:http://www.ccw.com.cn/server/yyjq/htm2005/20050817_15OF4.htm感谢原创作者 曹江华 Lin ...

  6. Linux下网卡驱动安装及故障排除

    Linux下网卡驱动安装及故障排除 赛迪网 2007-4-26 9:10:00文/forgiven 网卡是Linux服务器中最重要网络设备.据统计,Linux网络故障有35%在物理层.25%在数据链路 ...

  7. linux 查看网卡损坏,Linux服务器网卡驱动安装及故障排除

    Linux服务器网卡驱动安装及故障排除 网卡是Linux服务器中最重要网络设备.据统计,Linux网络故障有35%在物理层.25%在数据链路层.10%在网络层.10%在传输层.10%在对话.7%在表示 ...

  8. linux下u盘网卡驱动,通过u盘给linux安装网卡驱动的做法——深圳培训linux

    简单方便使用,随手可得. 简洁快速,只需一个u盘即可为linux系统安装网卡驱动. 简便快捷的方法都是受大众喜欢的,这样可以快速的简单的解决问题. 通过u盘给linux系统安装网卡驱动的方法,这是一个 ...

  9. linux下u盘网卡驱动,通过U盘给Linux系统安装网卡驱动的方法

    通过U盘给Linux系统安装网卡驱动的方法 RTL8169的网卡,系统是as 4.4 64位. 最精简安装,只安装了开发包. 没有驱动起来网卡,随机光盘没有for linux的网卡驱动. 只好到 rt ...

最新文章

  1. MEMS传感器作为变革的驱动力
  2. linux使用世界时间,linux世界里的时间
  3. 三星android pro,够大才能爽 三星Galaxy Note Pro评测
  4. vscode调试angular2
  5. “钉钉打卡神器”开发者被判五年半!
  6. Code Blocks 10 05的安装及使用
  7. CocoaChina2013开发者大会演讲稿-主会场-陈昊芝
  8. OpenCV-图像处理(03、Mat对象)
  9. Hadoop配置文件详解
  10. Tecplot360 作图经验
  11. Typora下载链接
  12. 柯马机器人示教器编程_COMAU柯马机器人示教器无显示维修过程
  13. 360wifi架设文件服务器,360wifi怎么搭建局域网共享平台
  14. 动态规划算法典型应用之背包问题
  15. Unity实现功能之局域网实时语音
  16. 为什么Word文档无响应,Word文档无响应的解决方法
  17. 动作捕捉技术对演员的演技诉求
  18. 更多改进 苹果iPhone 3G版深入评测
  19. html5拖拽表单设计器,require+jquery+backbone实现拖拽式报表设计器-拖拽式表单设计器...
  20. 移植OpenHarmony到星空派ARM芯片【1】

热门文章

  1. 自然语言处理(NLP)之word2vec的实现(PTB语料库)<找语义相近的词>
  2. 深度学习:线性回归模型
  3. ubuntu使用源下载库
  4. AD域控-漫游账户-同步中心
  5. 架构师如何应对复杂业务场景?领域建模的实战案例解析
  6. 在线教育平台开发需求分析
  7. 计算机股票分析报告,投资者分析股票市场报告和财务仪表板与商业智能 (Bi), 与关键绩效指标 (Kpi). 生意人手在大屏幕计算机上处理财务计划....
  8. 【通信】话务量的计算方法
  9. 如何恢复word文档模板和选项的默认设置
  10. Memblaze发布PBlaze 4系列PCIe SSD新品 全面拥抱 NVMe