gnet: 輕量級且高性能的 Golang 網絡庫

gnet

項目主頁

github.com/panjf2000/g…react

歡迎你們圍觀~~,目前還在持續更新,感興趣的話能夠 star 一下暗中觀察哦。linux

簡介

gnet 是一個基於 Event-Loop 事件驅動的高性能和輕量級網絡庫。這個庫直接使用 epollkqueue 系統調用而非標準 Golang 網絡包:net 來構建網絡應用,它的工做原理相似兩個開源的網絡庫:libuvlibeventgit

這個項目存在的價值是提供一個在網絡包處理方面能和 RedisHaproxy 這兩個項目具備相近性能的Go 語言網絡服務器框架。github

gnet 的亮點在於它是一個高性能、輕量級、非阻塞的純 Go 實現的傳輸層(TCP/UDP/Unix-Socket)網絡庫,開發者可使用 gnet 來實現本身的應用層網絡協議,從而構建出本身的應用層網絡應用:好比在 gnet 上實現 HTTP 協議就能夠建立出一個 HTTP 服務器 或者 Web 開發框架,實現 Redis 協議就能夠建立出本身的 Redis 服務器等等。golang

gnet 衍生自另外一個項目:evio,可是性能更好。redis

功能

  • 高性能 的基於多線程模型的 Event-Loop 事件驅動
  • 內置 Round-Robin 輪詢負載均衡算法
  • 簡潔的 APIs
  • 基於 Ring-Buffer 的高效內存利用
  • 支持多種網絡協議:TCP、UDP、Unix Sockets
  • 支持兩種事件驅動機制:Linux 裏的 epoll 以及 FreeBSD 裏的 kqueue
  • 支持異步寫操做
  • 容許多個網絡監聽地址綁定在一個 Event-Loop 上
  • 靈活的事件定時器
  • SO_REUSEPORT 端口重用

核心設計

多線程模型

gnet 從新設計開發了一個新內置的多線程模型:『主從 Reactor 多線程』,這也是 netty 默認的線程模型,下面是這個模型的原理圖:算法

multi_reactor

它的運行流程以下面的時序圖:shell

reactor

如今我正在 gnet 裏開發一個新的多線程模型:『帶線程/go程池的主從 Reactors 多線程』,而且很快就能完成,這個模型的架構圖以下所示:api

multi_reactor_thread_pool

它的運行流程以下面的時序圖:緩存

multi-reactors

通訊機制

gnet 的『主從 Reactors 多線程』模型是基於 Golang 裏的 Goroutines的,一個 Reactor 掛載在一個 Goroutine 上,因此在 gnet 的這個網絡模型裏主 Reactor/Goroutine 與從 Reactors/Goroutines 有海量通訊的需求,所以 gnet 裏必需要有一個能在 Goroutines 之間進行高效率的通訊的機制,我沒有選擇 Golang 裏的主流方案:基於 Channel 的 CSP 模型,而是選擇了性能更好、基於 Ring-Buffer 的 Disruptor 方案。

因此我最終選擇了 go-disruptor:高性能消息分發隊列 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

echolinux.png

HTTP Server

httplinux.png

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

echomac.png

HTTP Server

httpmac.png

證書

gnet 的源碼容許用戶在遵循 MIT 開源證書 規則的前提下使用。

待作事項

gnet 還在持續開發的過程當中,因此這個倉庫的代碼和文檔會一直持續更新,若是你對 gnet 感興趣的話,歡迎給這個開源庫貢獻你的代碼~~

相關文章
相關標籤/搜索