专栏首页编程札记gnet: 轻量级且高性能的 Golang 网络库

gnet: 轻量级且高性能的 Golang 网络库

项目主页

https://github.com/panjf2000/gnet

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

简介

gnet 是一个基于 Event-Loop 事件驱动的高性能和轻量级网络库。这个库直接使用 epoll[1] 和 kqueue[2] 系统调用而非标准 Golang 网络包:net[3] 来构建网络应用,它的工作原理类似两个开源的网络库:libuv[4] 和 libevent[5]。

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

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

gnet 衍生自另一个项目:evio,但是性能更好。

功能

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

核心设计

多线程模型

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

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

现在我正在 gnet 里开发一个新的多线程模型:『带线程/go程池的主从 Reactors 多线程』,并且很快就能完成,这个模型的架构图如下所示:

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

通信机制

gnet 的『主从 Reactors 多线程』模型是基于 Golang 里的 Goroutines的,一个 Reactor 挂载在一个 Goroutine 上,所以在 gnet 的这个网络模型里主 Reactor/Goroutine 与从 Reactors/Goroutines 有海量通信的需求,因此 gnet 里必须要有一个能在 Goroutines 之间进行高效率的通信的机制,我没有选择 Golang 里的主流方案:基于 Channel 的 CSP 模型,而是选择了性能更好、基于 Ring-Buffer 的 Disruptor 方案。

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

自动扩容的 Ring-Buffer

gnet 利用 Ring-Buffer 来缓存 TCP 流数据以及管理内存使用。

开始使用

安装

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

使用示例

// ======================== Echo Server implemented with gnet ===========================

package main

import (
    "flag"
    "fmt"
    "log"
    "strings"

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

func main() {
    var port int
    var loops int
    var udp bool
    var trace bool
    var reuseport bool

    flag.IntVar(&port, "port", 5000, "server port")
    flag.BoolVar(&udp, "udp", false, "listen on udp")
    flag.BoolVar(&reuseport, "reuseport", false, "reuseport (SO_REUSEPORT)")
    flag.BoolVar(&trace, "trace", false, "print packets to console")
    flag.IntVar(&loops, "loops", 0, "num loops")
    flag.Parse()

    var events gnet.Events
    events.NumLoops = loops
    events.OnInitComplete = func(srv gnet.Server) (action gnet.Action) {
        log.Printf("echo server started on port %d (loops: %d)", port, srv.NumLoops)
        if reuseport {
            log.Printf("reuseport")
        }
        return
    }
    events.React = func(c gnet.Conn, inBuf *ringbuffer.RingBuffer) (out []byte, action gnet.Action) {
        top, tail := inBuf.PreReadAll()
        out = append(top, tail...)
        inBuf.Reset()

        if trace {
            log.Printf("%s", strings.TrimSpace(string(top)+string(tail)))
        }
        return
    }
    scheme := "tcp"
    if udp {
        scheme = "udp"
    }
    log.Fatal(gnet.Serve(events, fmt.Sprintf("%s://:%d", scheme, port)))
}

I/O 事件

gnet 目前支持的 I/O 事件如下:

OnInitComplete 当 server 初始化完成之后调用。•OnOpened 当连接被打开的时候调用。•OnClosed 当连接被关闭的时候调用。•OnDetached 当主动摘除连接的时候的调用。•React 当 server 端接收到从 client 端发送来的数据的时候调用。(你的核心业务代码一般是写在这个方法里)•Tick 服务器启动的时候会调用一次,之后就以给定的时间间隔定时调用一次,是一个定时器方法。•PreWrite 预先写数据方法,在 server 端写数据回 client 端之前调用。

性能测试

Linux (epoll)

系统参数

Go Version: go1.12.9 linux/amd64
OS:         Ubuntu 18.04
CPU:        8 Virtual CPUs
Memory:     16.0 GiB

Echo Server

HTTP Server

FreeBSD (kqueue)

系统参数

Go Version: go version go1.12.9 darwin/amd64
OS:         macOS Mojave 10.14.6
CPU:        4 CPUs
Memory:     8.0 GiB

Echo Server

HTTP Server

证书

gnet 的源码允许用户在遵循 MIT 开源证书[10] 规则的前提下使用。

待做事项

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

References

[1] epoll: https://en.wikipedia.org/wiki/Epoll [2] kqueue: https://en.wikipedia.org/wiki/Kqueue [3] net: https://golang.org/pkg/net/ [4] libuv: https://github.com/libuv/libuv [5] libevent: https://github.com/libevent/libevent [6] Redis: http://redis.io [7] Haproxy: http://www.haproxy.org [8] 高性能: #性能测试 [9] go-disruptor: https://github.com/smartystreets-prototypes/go-disruptor [10] 开源证书: /https://github.com/panjf2000/gnet/blob/master/LICENSE

本文分享自微信公众号 - 潘建锋(R136a1_Pan),作者:潘建锋

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2019-09-16

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 【开源】gnet: 一个轻量级且高性能的 Golang 网络库

    gnet 是一个基于 Event-Loop 事件驱动的高性能和轻量级网络库。这个库直接使用 epoll 和 kqueue 系统调用而非标准 Golang 网络包...

    潘少
  • 最快的 Go 网络框架 gnet 来啦!

    gnet是一个基于事件驱动的高性能且轻量级的网络框架。它直接使用 epoll 和 kqueue 系统调用而非标准 Golang 网络包:net 来构建网络应用,...

    潘少
  • 【译】如何设计云原生应用的架构?

    「云原生 (Cloud Natvie)」 是一种将应用程序以微服务的形式构建并使之运行在容器化和动态编排平台之上的方式,这些平台充分利用了云计算模型的优势。「云...

    潘少
  • 【开源】gnet: 一个轻量级且高性能的 Golang 网络库

    gnet 是一个基于 Event-Loop 事件驱动的高性能和轻量级网络库。这个库直接使用 epoll 和 kqueue 系统调用而非标准 Golang 网络包...

    潘少
  • 谷歌面临欧洲监管机构创纪录罚款

    据“华尔街日报”周二报道,谷歌正面临欧盟委员会数十亿欧元的罚款。这次罚款的原因是谷歌涉嫌迫使智能手机厂商捆绑其应用程序。

    SDNLAB
  • 5G开放之路:理想、现实与思考

    近几年我的主要工作精力在SDN/NFV以及网络云化与智能化转型方面,在网络转型研究和实践中促使我一直在思考网络开放与通信行业发展相关问题。面对5G大潮和万物智联...

    SDNLAB
  • 开发部署移动APP如何选择腾讯云服务器配置?

    开发部署移动APP如何选择腾讯云服务器配置呢?随着移动互联网的飞速发展,智能手机的逐渐普及,现在大部分人用的手机都是智能手机,大家在手机上安装自己喜欢和常用的a...

    用户2416682
  • 开发部署移动APP如何选择腾讯云服务器配置?

    开发部署移动APP如何选择腾讯云服务器配置呢?随着移动互联网的飞速发展,智能手机的逐渐普及,现在大部分人用的手机都是智能手机,大家在手机上安装自己喜欢和常用的a...

    用户5920959
  • Maximum Depth of Binary Tree

    问题:二叉树的最深深度 class Solution { public: void dfs(TreeNode *root,int step,int &M...

    用户1624346
  • Oracle之SQL优化专题02-稳固SQL执行计划的方法

    糟糕的SQL执行,执行计划走全表扫描(这里实验直接利用使用hint强制不走索引来模拟这种情况):

    Alfred Zhao

扫码关注云+社区

领取腾讯云代金券