• Stars
    star
    545
  • Rank 78,442 (Top 2 %)
  • Language
    Go
  • License
    MIT License
  • Created over 4 years ago
  • Updated 3 months ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

High performance async-io(proactor) networking for Golang。golangのための高性能非同期io(proactor)ネットワーキング

gaio

GoDoc MIT licensed Build Status Go Report Card Coverage Statusd

gaio

Introduction

中文介绍

For a typical golang network program, you would first conn := lis.Accept() to get a connection and go func(net.Conn) to start a goroutine for handling the incoming data, then you would buf:=make([]byte, 4096) to allocate some buffer and finally waits on conn.Read(buf).

For a server holding >10K connections with frequent short messages(e.g. < 512B), cost for context switching is much more expensive than receiving message(a context switch needs at least 1000 CPU cycles or 600ns on 2.1GHz).

And by eliminating one goroutine per one connection scheme with Edge-Triggered IO Multiplexing, the 2KB(R)+2KB(W) per connection goroutine stack can be saved. By using internal swap buffer, buf:=make([]byte, 4096) can be saved(at the cost of performance).

gaio is an proactor pattern networking library satisfy both memory constraints and performance goals.

Features

  1. Tested in High Frequency Trading for handling HTTP requests for 30K~40K RPS on a single HVM server.
  2. Designed for >C10K concurrent connections, maximized parallelism, and nice single connection throughput.
  3. Read(ctx, conn, buffer) can be called with nil buffer to make use of internal swap buffer.
  4. Non-intrusive design, this library works with net.Listener and net.Conn. (with syscall.RawConn support), easy to be integrated into your existing software.
  5. Amortized context switching cost for tiny messages, able to handle frequent chat message exchanging.
  6. Application can decide when to delegate net.Conn to gaio, for example, you can delegate net.Conn to gaio after some handshaking procedure, or having some net.TCPConn settings done.
  7. Application can decide when to submit read or write requests, per-connection back-pressure can be propagated to peer to slow down sending. This features is particular useful to transmit data from A to B via gaio, which B is slower than A.
  8. Tiny, around 1000 LOC, easy to debug.
  9. Support for Linux, BSD.

Conventions

  1. Once you submit an async read/write requests with related net.Conn to gaio.Watcher, this conn will be delegated to gaio.Watcher at first submit. Future use of this conn like conn.Read or conn.Write will return error, but TCP properties set by SetReadBuffer(), SetWriteBuffer(), SetLinger(), SetKeepAlive(), SetNoDelay() will be inherited.
  2. If you decide not to use this connection anymore, you could call Watcher.Free(net.Conn) to close socket and free related resources immediately.
  3. If you forget to call Watcher.Free(net.Conn), runtime garbage collector will cleanup related system resources if nowhere in the system holds the net.Conn.
  4. If you forget to call Watcher.Close(), runtime garbage collector will cleanup ALL related system resources if nowhere in the system holds this Watcher.
  5. For connection Load-Balance, you can create multiple gaio.Watcher with your own strategy to distribute net.Conn.
  6. For acceptor Load-Balance, you can use go-reuseport as the listener.
  7. For read requests submitted with 'nil' buffer, the returning []byte from Watcher.WaitIO() is SAFE to use before next call to Watcher.WaitIO() returned.

TL;DR

package main

import (
        "log"
        "net"

        "github.com/xtaci/gaio"
)

// this goroutine will wait for all io events, and sents back everything it received
// in async way
func echoServer(w *gaio.Watcher) {
        for {
                // loop wait for any IO events
                results, err := w.WaitIO()
                if err != nil {
                        log.Println(err)
                        return
                }

                for _, res := range results {
                        switch res.Operation {
                        case gaio.OpRead: // read completion event
                                if res.Error == nil {
                                        // send back everything, we won't start to read again until write completes.
                                        // submit an async write request
                                        w.Write(nil, res.Conn, res.Buffer[:res.Size])
                                }
                        case gaio.OpWrite: // write completion event
                                if res.Error == nil {
                                        // since write has completed, let's start read on this conn again
                                        w.Read(nil, res.Conn, res.Buffer[:cap(res.Buffer)])
                                }
                        }
                }
        }
}

func main() {
        w, err := gaio.NewWatcher()
        if err != nil {
              log.Fatal(err)
        }
        defer w.Close()
	
        go echoServer(w)

        ln, err := net.Listen("tcp", "localhost:0")
        if err != nil {
                log.Fatal(err)
        }
        log.Println("echo server listening on", ln.Addr())

        for {
                conn, err := ln.Accept()
                if err != nil {
                        log.Println(err)
                        return
                }
                log.Println("new client", conn.RemoteAddr())

                // submit the first async read IO request
                err = w.Read(nil, conn, make([]byte, 128))
                if err != nil {
                        log.Println(err)
                        return
                }
        }
}

More examples

Push server package main
package main

import (
        "fmt"
        "log"
        "net"
        "time"

        "github.com/xtaci/gaio"
)

func main() {
        // by simply replace net.Listen with reuseport.Listen, everything is the same as in push-server
        // ln, err := reuseport.Listen("tcp", "localhost:0")
        ln, err := net.Listen("tcp", "localhost:0")
        if err != nil {
                log.Fatal(err)
        }

        log.Println("pushing server listening on", ln.Addr(), ", use telnet to receive push")

        // create a watcher
        w, err := gaio.NewWatcher()
        if err != nil {
                log.Fatal(err)
        }

        // channel
        ticker := time.NewTicker(time.Second)
        chConn := make(chan net.Conn)
        chIO := make(chan gaio.OpResult)

        // watcher.WaitIO goroutine
        go func() {
                for {
                        results, err := w.WaitIO()
                        if err != nil {
                                log.Println(err)
                                return
                        }

                        for _, res := range results {
                                chIO <- res
                        }
                }
        }()

        // main logic loop, like your program core loop.
        go func() {
                var conns []net.Conn
                for {
                        select {
                        case res := <-chIO: // receive IO events from watcher
                                if res.Error != nil {
                                        continue
                                }
                                conns = append(conns, res.Conn)
                        case t := <-ticker.C: // receive ticker events
                                push := []byte(fmt.Sprintf("%s\n", t))
                                // all conn will receive the same 'push' content
                                for _, conn := range conns {
                                        w.Write(nil, conn, push)
                                }
                                conns = nil
                        case conn := <-chConn: // receive new connection events
                                conns = append(conns, conn)
                        }
                }
        }()

        // this loop keeps on accepting connections and send to main loop
        for {
                conn, err := ln.Accept()
                if err != nil {
                        log.Println(err)
                        return
                }
                chConn <- conn
        }
}

Documentation

For complete documentation, see the associated Godoc.

Benchmarks

Test Case Throughput test with 64KB buffer
Description A client keep on sending 64KB bytes to server, server keeps on reading and sending back whatever it received, the client keeps on receiving whatever the server sent back until all bytes received successfully
Command go test -v -run=^$ -bench Echo
Macbook Pro 1695.27 MB/s 518 B/op 4 allocs/op
Linux AMD64 1883.23 MB/s 518 B/op 4 allocs/op
Raspberry Pi4 354.59 MB/s 334 B/op 4 allocs/op
Test Case 8K concurrent connection echo test
Description Start 8192 clients, each client send 1KB data to server, server keeps on reading and sending back whatever it received, the client keeps on receiving whatever the server sent back until all bytes received successfully.
Command go test -v -run=8k
Macbook Pro 1.09s
Linux AMD64 0.94s
Raspberry Pi4 2.09s

Regression

regression

X -> number of concurrent connections, Y -> time of completion in seconds

Best-fit values	 
Slope	8.613e-005 ± 5.272e-006
Y-intercept	0.08278 ± 0.03998
X-intercept	-961.1
1/Slope	11610
 
95% Confidence Intervals	 
Slope	7.150e-005 to 0.0001008
Y-intercept	-0.02820 to 0.1938
X-intercept	-2642 to 287.1
 
Goodness of Fit	 
R square	0.9852
Sy.x	0.05421
 
Is slope significantly non-zero?	 
F	266.9
DFn,DFd	1,4
P Value	< 0.0001
Deviation from horizontal?	Significant
 
Data	 
Number of XY pairs	6
Equation	Y = 8.613e-005*X + 0.08278

License

gaio source code is available under the MIT License.

References

Status

Stable

More Repositories

1

kcptun

A Stable & Secure Tunnel based on KCP with N:M multiplexing and FEC. Available for ARM, MIPS, 386 and AMD64。N:M 多重化と FEC を備えた KCP に基づく安定した安全なトンネル。 N:M 다중화 및 FEC를 사용하는 KCP 기반의 안정적이고 안전한 터널입니다. Un tunnel stable et sécurisé basé sur KCP avec multiplexage N:M et FEC.
Go
13,605
star
2

algorithms

Algorithms & Data structures in C++.
C++
5,115
star
3

kcp-go

A Crypto-Secure, Production-Grade Reliable-UDP Library for golang with FEC
Go
3,879
star
4

gonet

A Game Server Skeleton in golang.
Go
1,243
star
5

smux

A Stream Multiplexing Library for golang with least memory usage(TDMA)
Go
1,214
star
6

libkcp

FEC enhanced KCP session library for iOS/Android in C++
C
293
star
7

tcpraw

Sending packets through TCP
Go
125
star
8

safebox

One key to derive all
Go
55
star
9

buddha

佛教资料汇集
54
star
10

navmesh

navigation mesh in golang
Go
41
star
11

sp

Stream Processors on Kafka in Golang
Go
29
star
12

chat

pub/sub based chat server
Go
27
star
13

rank

ranking server
Go
23
star
14

sstable

bigdata processing in golang
Go
23
star
15

lossyconn

lossy connection simulator
Go
20
star
16

rewind

Text-Based UI for Kafka
Go
20
star
17

goeval

eval golang code on the fly
Go
15
star
18

log_analysis

Practical Log Analysis
15
star
19

fibernet

Message Queue/C++/Lua based game server
C
15
star
20

wsl-best-practice

best practice for development environment in WSL
14
star
21

notes

personal notes
Go
12
star
22

gogw

Go
9
star
23

auth

auth service
Go
8
star
24

goperf

golang performance benchmarks
Go
7
star
25

bgsave

background save process of redis
Go
7
star
26

serialpacket

net.PacketConn over RS232/LoRa
Go
6
star
27

reorg

A simulated LFN network to mitigate network jitter, reorg trade latency in exchange for smoothness, so as to behave like a long fat but stable network.
Go
5
star
28

tmach

turing machine game
Go
5
star
29

xtaci

4
star
30

json2hive

generate hive schema from a json document
Go
4
star
31

chacha20

an exposed version of https://godoc.org/golang.org/x/crypto/internal/chacha20
Go
4
star
32

kidsmath

a simple program to generate math quizs for my kid.
Go
3
star
33

archiver

redolog archive and replay
Go
3
star
34

serial2tun

Serial To Tun Device
3
star
35

easenet

Automatically exported from code.google.com/p/easenet
C
3
star
36

zturn

(zturn:折腾) a free game brings you back to 1980s
3
star
37

goscm

simple scheme interpreter
Go
1
star
38

poly2tri.as3

Automatically exported from code.google.com/p/poly2tri.as3
ActionScript
1
star
39

logrushooks

hooks for logrus
Go
1
star
40

poly2tri

Automatically exported from code.google.com/p/poly2tri
C++
1
star
41

godeep

machine learning algorithms
1
star
42

ssh-kvr

lex/yacc learning
C
1
star
43

deadlocks

deadlock code snippets in C
C
1
star
44

algebra

notes on algebra learning
1
star
45

log4go

Automatically exported from code.google.com/p/log4go
Go
1
star
46

debris

Shamir's Secret Sharing
1
star
47

ethereum_indexer

A project for indexing and querying ethereum accounts
Go
1
star