歡迎你們圍觀~~,目前還在持續更新,感興趣的話能夠 star 一下暗中觀察哦。linux
gnet
是一個基於 Event-Loop 事件驅動的高性能和輕量級網絡庫。這個庫直接使用 epoll 和 kqueue 系統調用而非標準 Golang 網絡包:net 來構建網絡應用,它的工做原理相似兩個開源的網絡庫:libuv 和 libevent。git
這個項目存在的價值是提供一個在網絡包處理方面能和 Redis、Haproxy 這兩個項目具備相近性能的Go 語言網絡服務器框架。github
gnet
的亮點在於它是一個高性能、輕量級、非阻塞的純 Go 實現的傳輸層(TCP/UDP/Unix-Socket)網絡庫,開發者可使用 gnet
來實現本身的應用層網絡協議,從而構建出本身的應用層網絡應用:好比在 gnet
上實現 HTTP 協議就能夠建立出一個 HTTP 服務器 或者 Web 開發框架,實現 Redis 協議就能夠建立出本身的 Redis 服務器等等。golang
gnet
衍生自另外一個項目:evio
,可是性能更好。redis
gnet
從新設計開發了一個新內置的多線程模型:『主從 Reactor 多線程』,這也是 netty
默認的線程模型,下面是這個模型的原理圖:算法
它的運行流程以下面的時序圖:shell
如今我正在 gnet
裏開發一個新的多線程模型:『帶線程/go程池的主從 Reactors 多線程』,而且很快就能完成,這個模型的架構圖以下所示:api
它的運行流程以下面的時序圖:緩存
gnet
的『主從 Reactors 多線程』模型是基於 Golang 裏的 Goroutines的,一個 Reactor 掛載在一個 Goroutine 上,因此在 gnet
的這個網絡模型裏主 Reactor/Goroutine 與從 Reactors/Goroutines 有海量通訊的需求,所以 gnet
裏必需要有一個能在 Goroutines 之間進行高效率的通訊的機制,我沒有選擇 Golang 裏的主流方案:基於 Channel 的 CSP 模型,而是選擇了性能更好、基於 Ring-Buffer 的 Disruptor 方案。
因此我最終選擇了 go-disruptor:高性能消息分發隊列 LMAX Disruptor 的 Golang 實現。
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)))
}
複製代碼
gnet
目前支持的 I/O 事件以下:
OnInitComplete
當 server 初始化完成以後調用。OnOpened
當鏈接被打開的時候調用。OnClosed
當鏈接被關閉的時候調用。OnDetached
當主動摘除鏈接的時候的調用。React
當 server 端接收到從 client 端發送來的數據的時候調用。(你的核心業務代碼通常是寫在這個方法裏)Tick
服務器啓動的時候會調用一次,以後就以給定的時間間隔定時調用一次,是一個定時器方法。PreWrite
預先寫數據方法,在 server 端寫數據回 client 端以前調用。Go Version: go1.12.9 linux/amd64
OS: Ubuntu 18.04
CPU: 8 Virtual CPUs
Memory: 16.0 GiB
複製代碼
Go Version: go version go1.12.9 darwin/amd64
OS: macOS Mojave 10.14.6
CPU: 4 CPUs
Memory: 8.0 GiB
複製代碼
gnet
的源碼容許用戶在遵循 MIT 開源證書 規則的前提下使用。
gnet 還在持續開發的過程當中,因此這個倉庫的代碼和文檔會一直持續更新,若是你對 gnet 感興趣的話,歡迎給這個開源庫貢獻你的代碼~~