• Stars
    star
    808
  • Rank 56,429 (Top 2 %)
  • Language
    Go
  • License
    MIT License
  • Created almost 5 years ago
  • Updated about 1 month ago

Reviews

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

Repository Details

High performance minimalism async-io(proactor) networking for Golang.

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 Quantum-Safe Secure Tunnel based on QPP, KCP, FEC, and N:M multiplexing.
Go
13,904
star
2

algorithms

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

kcp-go

A Crypto-Secure Reliable-UDP Library for golang with FEC
Go
4,086
star
4

smux

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

gonet

A Game Server Skeleton in golang.
Go
1,264
star
6

libkcp

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

tcpraw

Sending packets through TCP
Go
137
star
8

buddha

佛教资料汇集
63
star
9

safebox

One key to derive all
Go
56
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

lossyconn

lossy connection simulator
Go
24
star
14

rank

ranking server
Go
23
star
15

sstable

bigdata processing in golang
Go
23
star
16

rewind

Text-Based UI for Kafka
Go
20
star
17

qpp

Quantum Permutation Pad (QPP)
Go
18
star
18

goeval

eval golang code on the fly
Go
15
star
19

log_analysis

Practical Log Analysis
15
star
20

fibernet

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

wsl-best-practice

best practice for development environment in WSL
14
star
22

notes

personal notes
Go
12
star
23

hppk

Homomorphic Polynomial Public Key
Go
11
star
24

gogw

Go
9
star
25

auth

auth service
Go
9
star
26

bgsave

background save process of redis
Go
7
star
27

goperf

golang performance benchmarks
Go
7
star
28

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
6
star
29

serialpacket

net.PacketConn over RS232/LoRa
Go
6
star
30

easenet

Automatically exported from code.google.com/p/easenet
C
4
star
31

xtaci

4
star
32

json2hive

generate hive schema from a json document
Go
4
star
33

chacha20

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

tmach

turing machine game
Go
4
star
35

dppk

A Deterministic Polynomial Public Key Algorithm over a Prime Galois Field GF(p)
Go
4
star
36

kidsmath

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

archiver

redolog archive and replay
Go
3
star
38

serial2tun

Serial To Tun Device
3
star
39

zturn

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

goscm

simple scheme interpreter
Go
1
star
41

poly2tri.as3

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

logrushooks

hooks for logrus
Go
1
star
43

ssh-kvr

lex/yacc learning
C
1
star
44

poly2tri

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

godeep

machine learning algorithms
1
star
46

deadlocks

deadlock code snippets in C
C
1
star
47

algebra

notes on algebra learning
1
star
48

log4go

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

debris

Shamir's Secret Sharing
1
star
50

ethereum_indexer

A project for indexing and querying ethereum accounts
Go
1
star