gain 基于io_uring 的golang 网络框架

发布时间 2023-07-10 10:03:08作者: 荣锋亮

io_uring 是linux kernel 从5.1开始支持的新的io 操作模式,性能很不错,gain 是一个golang 实现

参考使用

  • main.go
package main
 
import (
    "fmt"
    "log"
    "net"
    "os"
    "sync/atomic"
    "time"
 
    "github.com/pawelgaczynski/gain"
    "github.com/pawelgaczynski/gain/logger"
    "github.com/rs/zerolog"
)
 
const (
    port            = 8080
    numberOfClients = 2
)
 
var testData = []byte("echo")
 
type EventHandler struct {
    server gain.Server
 
    logger zerolog.Logger
 
    overallBytesSent atomic.Uint64
}
 
func (e *EventHandler) OnStart(server gain.Server) {
    e.server = server
    e.logger = zerolog.New(os.Stdout).With().Logger().Level(zerolog.InfoLevel)
}
 
func (e *EventHandler) OnAccept(conn gain.Conn) {
    e.logger.Info().
        Int("active connections", e.server.ActiveConnections()).
        Str("remote address", conn.RemoteAddr().String()).
        Msg("New connection accepted")
}
 
func (e *EventHandler) OnRead(conn gain.Conn, n int) {
    e.logger.Info().
        Int("bytes", n).
        Str("remote address", conn.RemoteAddr().String()).
        Msg("Bytes received from remote peer")
 
    var (
        err    error
        buffer []byte
    )
 
    buffer, err = conn.Next(n)
    if err != nil {
        return
    }
 
    _, _ = conn.Write(buffer)
}
 
func (e *EventHandler) OnWrite(conn gain.Conn, n int) {
    e.overallBytesSent.Add(uint64(n))
 
    e.logger.Info().
        Int("bytes", n).
        Str("remote address", conn.RemoteAddr().String()).
        Msg("Bytes sent to remote peer")
 
    err := conn.Close()
    if err != nil {
        e.logger.Error().Err(err).Msg("Error during connection close")
    }
}
 
func (e *EventHandler) OnClose(conn gain.Conn, err error) {
    log := e.logger.Info().
        Str("remote address", conn.RemoteAddr().String())
    if err != nil {
        log.Err(err).Msg("Connection from remote peer closed")
    } else {
        log.Msg("Connection from remote peer closed by server")
    }
 
    if e.overallBytesSent.Load() >= uint64(len(testData)*numberOfClients) {
        e.server.AsyncShutdown()
    }
}
 
func runClients() {
    for i := 0; i < numberOfClients; i++ {
        go func() {
            time.Sleep(time.Second)
 
            conn, err := net.DialTimeout("tcp", fmt.Sprintf("127.0.0.1:%d", port), time.Second)
            if err != nil {
                log.Panic(err)
            }
 
            n, err := conn.Write(testData)
            if err != nil {
                log.Panic()
            }
 
            if n != len(testData) {
                log.Panic()
            }
 
            buffer := make([]byte, len(testData))
 
            n, err = conn.Read(buffer)
            if err != nil {
                log.Panic()
            }
 
            if n != len(testData) {
                log.Panic()
            }
        }()
    }
}
 
func main() {
    // runClients()
 
    err := gain.ListenAndServe(
        fmt.Sprintf("tcp://0.0.0.0:%d", port), &EventHandler{}, gain.WithLoggerLevel(logger.WarnLevel))
    if err != nil {
        log.Panic(err)
    }
}

以上代码会创建server,监听在8080 端口,对于测试,可以通过telnet 发送echo 数据,如果有异常的服务会关闭

说明

基于io_uring 的网络框架已经有不少了,但是目前国内一般linux kernel 版本都不太高,所以基本上使用是有些问题的

参考资料

https://github.com/pawelgaczynski/gain
https://en.wikipedia.org/wiki/Io_uring
https://developers.redhat.com/articles/2023/04/12/why-you-should-use-iouring-network-io
https://medium.com/better-programming/an-introduction-to-gain-part-1-writing-high-performance-tcp-application-df5f7253e54a
https://betterprogramming.pub/gain-the-new-fastest-go-tcp-framework-40ec111d40e6
https://blog.vmsplice.net/2020/07/rethinking-event-loop-integration-for.html