Fast event-loop networking for Go

Related tags

Network networking
Overview

evio
GoDoc

evio is an event loop networking framework that is fast and small. It makes direct epoll and kqueue syscalls rather than using the standard Go net package, and works in a similar manner as libuv and libevent.

The goal of this project is to create a server framework for Go that performs on par with Redis and Haproxy for packet handling. It was built to be the foundation for Tile38 and a future L7 proxy for Go.

Please note: Evio should not be considered as a drop-in replacement for the standard Go net or net/http packages.

Features

Getting Started

Installing

To start using evio, install Go and run go get:

$ go get -u github.com/tidwall/evio

This will retrieve the library.

Usage

Starting a server is easy with evio. Just set up your events and pass them to the Serve function along with the binding address(es). Each connections is represented as an evio.Conn object that is passed to various events to differentiate the clients. At any point you can close a client or shutdown the server by return a Close or Shutdown action from an event.

Example echo server that binds to port 5000:

package main

import "github.com/tidwall/evio"

func main() {
	var events evio.Events
	events.Data = func(c evio.Conn, in []byte) (out []byte, action evio.Action) {
		out = in
		return
	}
	if err := evio.Serve(events, "tcp://localhost:5000"); err != nil {
		panic(err.Error())
	}
}

Here the only event being used is Data, which fires when the server receives input data from a client. The exact same input data is then passed through the output return value, which is then sent back to the client.

Connect to the echo server:

$ telnet localhost 5000

Events

The event type has a bunch of handy events:

  • Serving fires when the server is ready to accept new connections.
  • Opened fires when a connection has opened.
  • Closed fires when a connection has closed.
  • Detach fires when a connection has been detached using the Detach return action.
  • Data fires when the server receives new data from a connection.
  • Tick fires immediately after the server starts and will fire again after a specified interval.

Multiple addresses

A server can bind to multiple addresses and share the same event loop.

evio.Serve(events, "tcp://192.168.0.10:5000", "unix://socket")

Ticker

The Tick event fires ticks at a specified interval. The first tick fires immediately after the Serving events.

events.Tick = func() (delay time.Duration, action Action){
	log.Printf("tick")
	delay = time.Second
	return
}

UDP

The Serve function can bind to UDP addresses.

  • All incoming and outgoing packets are not buffered and sent individually.
  • The Opened and Closed events are not availble for UDP sockets, only the Data event.

Multithreaded

The events.NumLoops options sets the number of loops to use for the server. A value greater than 1 will effectively make the server multithreaded for multi-core machines. Which means you must take care when synchonizing memory between event callbacks. Setting to 0 or 1 will run the server as single-threaded. Setting to -1 will automatically assign this value equal to runtime.NumProcs().

Load balancing

The events.LoadBalance options sets the load balancing method. Load balancing is always a best effort to attempt to distribute the incoming connections between multiple loops. This option is only available when events.NumLoops is set.

  • Random requests that connections are randomly distributed.
  • RoundRobin requests that connections are distributed to a loop in a round-robin fashion.
  • LeastConnections assigns the next accepted connection to the loop with the least number of active connections.

SO_REUSEPORT

Servers can utilize the SO_REUSEPORT option which allows multiple sockets on the same host to bind to the same port.

Just provide reuseport=true to an address:

evio.Serve(events, "tcp://0.0.0.0:1234?reuseport=true"))

More examples

Please check out the examples subdirectory for a simplified redis clone, an echo server, and a very basic http server.

To run an example:

$ go run examples/http-server/main.go
$ go run examples/redis-server/main.go
$ go run examples/echo-server/main.go

Performance

Benchmarks

These benchmarks were run on an ec2 c4.xlarge instance in single-threaded mode (GOMAXPROC=1) over Ipv4 localhost. Check out benchmarks for more info.

echo benchmarkhttp benchmarkredis 1 benchmarkredis 8 benchmark

Contact

Josh Baker @tidwall

License

evio source code is available under the MIT License.

Comments
  • Make sure it only do the signalShutdown once

    Make sure it only do the signalShutdown once

    Use sync.Once to make sure it would only call signalShutdow once when trying to shutdown evio cuz I found that it would call this function as many times as the number of loops and listeners, which is the redundant cost.

    opened by panjf2000 9
  • Info structure in events.Opened is empty

    Info structure in events.Opened is empty

    Code:

    events.Opened = func(id int, info evio.Info) (out []byte, opts evio.Options, action evio.Action) {
        log.Printf("opened: %d: %v\n", id, info.RemoteAddr)
        return
      }
    

    Output:

    opened: 1: <nil>
    
    opened by dgrr 7
  • Reduce at least one syscall.EPOLLOUT event in every connection

    Reduce at least one syscall.EPOLLOUT event in every connection

    Reduce at least one syscall.EPOLLOUT event in every connection, enhancing a bit of performance.

    Benchmark between this branch and master:

    bench2 bench3 bench4

    Benchmark test in Amazon EC2:

    c5.2xlarge (8 Virtual CPUs, 16.0 GiB Memory, 120 GiB SSD (EBS) Storage)
    gcc version 7.2.1 20170915 (Red Hat 7.2.1-2) (GCC)
    
    opened by panjf2000 6
  • Saw this... but why not?

    Saw this... but why not?

    You would not want to use this framework if you need to handle long-running requests (milliseconds or more). For example, a web api that needs to connect to a mongo database, authenticate, and respond; just use the Go net/http package instead.

    Why not? What will be the problem if I do so? Most request will be sub milliseconds but just one or two will be in the milliseconds... but I'm curious what will the problem be... can you elaborate?

    opened by unisqu 6
  • CPU consuming 100%

    CPU consuming 100%

    I'm not really sure why but after a few hours of running with minimum connection ( around 100 ). CPU usage starts growing up to 100%. here is profile.

    (pprof) top5 -cum
    Showing nodes accounting for 9.86hrs, 79.77% of 12.36hrs total
    Dropped 348 nodes (cum <= 0.06hrs)
    Showing top 5 nodes out of 23
          flat  flat%   sum%        cum   cum%
             0     0%     0%   12.02hrs 97.25%  github.com/tidwall/evio.loopRun
       0.17hrs  1.40%  1.40%   12.02hrs 97.25%  github.com/tidwall/evio/internal.(*Poll).Wait
       0.08hrs  0.61%  2.01%   11.30hrs 91.41%  syscall.EpollWait
       9.57hrs 77.45% 79.46%   11.22hrs 90.80%  syscall.Syscall6
       0.04hrs  0.31% 79.77%    0.88hrs  7.13%  runtime.entersyscall
    

    Graph: 2020-01-03 10_19_50-Window

    Do you have any ideas about why it happens?

    opened by namkazt 5
  • About the Reactor pattern

    About the Reactor pattern

    hi Josh Can you explain where the Reactor pattern is used in this project? I understand that the Reactor pattern and the Proactor pattern are implemented by the kernel, not the things that the application layer needs to worry about. Maybe I don't understand. I'm looking forward to your explanation.

    opened by Luncher 4
  • How to write UDP?

    How to write UDP?

    #13 shows how to use Wake to write something, but comment of Wake says:

    Not available for UDP connections.

    Like i receive a tcp connection, how can i write to another udp conn?

    opened by ayanamist 3
  • Re-implementation of the go scheduler

    Re-implementation of the go scheduler

    The go goroutine scheduler already uses epoll/kqueue to decide when goroutines need to be scheduled.

    Why is there a need to re-implement this, am I missing something?

    opened by noonien 3
  • Name clash

    Name clash

    Hey, thanks for the cool project. The name is conflicting with Go experimental UI lib, though. https://github.com/golang/exp/tree/master/shiny

    I think in Go community name "shiny" is already strongly linked to this lib.

    Opening this issue just to raise the concern as early as possible, because changing the name is easier at the early stage of project.

    opened by divan 3
  • numLoops > 1 will make udp server slower

    numLoops > 1 will make udp server slower

    Hi: I found when set numLoops > 1, udp server with evio will be slower than numLoops = 1. Use profile, I found there is too much syscall in loopUDPRead(s, l, i, fd).
    I add a debug code fmt.Println("-- loop udp read --", l.idx) before loopUDPRead, then send just one udp msg to it, there are numLoops's print in stdout. I change the LoadBalance to AtLeastConnections / RoundRobin, there is also more call to loopUDPRead when numLoops > 1.

    opened by tianlin 2
  • Fix Poll.Trigger byte order bug under linux

    Fix Poll.Trigger byte order bug under linux

    See http://man7.org/linux/man-pages/man2/eventfd.2.html Bytes written into an eventfd object should have the same endianness as the host machine. Here I assume Poll.Trigger tends to incr by 1, not 1 << 56.

    opened by coyove 2
  • Differences with other libraries

    Differences with other libraries

    Since I consider evio the first/old school event loop libraries for Go I wonder what is the difference between all these newer ones like: https://github.com/panjf2000/gnet https://github.com/hslam/netpoll https://github.com/xtaci/gaio https://github.com/Allenxuxu/gev

    It seems like they are all inspired by evio and then by one another so I am just curious to understand if there is some unique feature in these or if they are all one ant the same in principle and in performance?

    In the end, I am quite confused to figure out which one to pick.

    opened by ivanjaros 1
  • [question] why use count field in loop not len(fdconns)?

    [question] why use count field in loop not len(fdconns)?

    
    type loop struct {
    	idx     int            // loop index in the server loops list
    	poll    *internal.Poll // epoll or kqueue
    	packet  []byte         // read packet buffer
    	fdconns map[int]*conn  // loop connections fd -> conn
    	count   int32          // connection count
    }
    
    

    why not use len(fdconns) instead of count?

    opened by Ccheers 0
  • Calling wake should append c.out, instead of saving only last output call

    Calling wake should append c.out, instead of saving only last output call

    https://github.com/tidwall/evio/blob/c72befaf8b4ff9419bcd8b150f582a358bebf33b/evio_unix.go#L421 should be c.out = append(c.out, out...), otherwise if calling multiple wake before loopWrite is called, only the last output is sent

    opened by rubihouri-anzu 0
  • In the linux tcp epoll mode, some conditions will lead to starvation when reading, as well as data loss and repeated sending problems in the buffer to be written in the connection

    In the linux tcp epoll mode, some conditions will lead to starvation when reading, as well as data loss and repeated sending problems in the buffer to be written in the connection

    The three questions are as follows: 1. When writing data to fd through the loopWrite function, you should check the number of successfully written bytes returned, because when writing some bytes successfully, if the write buffer of fd is full, err=syscall will also be returned .EAGAIN, which leads to repeated data writing next time. When the syscall.Write() function returns err, judge whether n is equal to -1, so as to slice the buffer of partially written data to avoid the next repetition Write.

    2. When writing data to fd through the loopWrite function returns err=syscall.EAGAIN, call the Wake() function of conn to wake up the connection, wake up the Data() function of evio.Events in loopWake(), and return the data to be written to the connection, if the conn was previously .out has a buffer to be written, which will cause the previous buffer data in conn.out to be lost.

    3. When writing data to fd through the loopWrite function returns err=syscall.EAGAIN, continue to call the Wake() function of conn to wake up the connection, wake up the Data() function of evio.Events in loopWake(), and return the large batch of data to be written by the connection, if The write buffer of fd has space to write. At this time, the fd is also triggered by a read event. When each epoll is triggered, only the loopWrite function will be processed, and err=syscall.EAGAIN will be returned when writing, which is the waiting time of fd. All data in the write buffer is not successfully written to fd, at this time the connection will be in a state of continuous writing, and each time epoll triggers the phenomenon that the connection read event is not processed, causing the fd read processing to starve .Note: This phenomenon should rarely happen, but I think it may happen if you do this

    opened by zjs604381586 1
  • Fix typo in event.Tick example

    Fix typo in event.Tick example

    Fix typo in event.Tick example in README

    -events.Tick = func() (delay time.Duration, action Action){
    +events.Tick = func() (delay time.Duration, action evio.Action){
            log.Printf("tick")
            delay = time.Second
            return
    opened by msquee 0
Owner
Josh
Josh
🚀 gnet is a high-performance, lightweight, non-blocking, event-driven networking framework written in pure Go./ gnet 是一个高性能、轻量级、非阻塞的事件驱动 Go 网络框架。

English | ???? 中文 ?? Introduction gnet is an event-driven networking framework that is fast and lightweight. It makes direct epoll and kqueue syscalls

Andy Pan 7.2k Jan 2, 2023
High-performance, non-blocking, event-driven, easy-to-use networking framework written in Go, support tls/http1.x/websocket.

High-performance, non-blocking, event-driven, easy-to-use networking framework written in Go, support tls/http1.x/websocket.

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

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

xtaci 474 Dec 29, 2022
Packiffer is a lightweight cross-platform networking toolkit that let you sniff/analyze/inject/filter packets.

Packiffer is a lightweight cross-platform networking toolkit that let you sniff/analyze/inject/filter packets.

Massoud Asadi 62 Dec 19, 2022
A decentralized P2P networking stack written in Go.

noise noise is an opinionated, easy-to-use P2P network stack for decentralized applications, and cryptographic protocols written in Go. noise is made

Perlin Network 1.7k Dec 29, 2022
Netpoll is a high-performance non-blocking I/O networking framework, which focused on RPC scenarios, developed by ByteDance.

Netpoll is a high-performance non-blocking I/O networking framework, which focused on RPC scenarios, developed by ByteDance. RPC is usually heavy on processing logic and therefore cannot handle I/O serially. But Go's standard library net designed blocking I/O API, so that the RPC framework can only follow the One Conn One Goroutine design.

CloudWeGo 3.3k Jan 2, 2023
Fork of Go stdlib's net/http that works with alternative TLS libraries like refraction-networking/utls.

github.com/ooni/oohttp This repository contains a fork of Go's standard library net/http package including patches to allow using this HTTP code with

Open Observatory of Network Interference (OONI) 30 Sep 29, 2022
🧪 Run common networking tests against your site.

dstp dstp, run common networking tests against your site. Usage Usage: dstp [OPTIONS] [ARGS]

Yagiz Degirmenci 919 Jan 3, 2023
Hybridnet is an open source container networking solution, integrated with Kubernetes and used officially by following well-known PaaS platforms

Hybridnet What is Hybridnet? Hybridnet is an open source container networking solution, integrated with Kubernetes and used officially by following we

Alibaba 164 Jan 4, 2023
Basic Got chat program using Ably for networking

Go Terminal Chat Basic Got chat program using Ably for networking. Taken from GopherCon UK 2021: Tom Camp - Creating a basic chat app. Setup Replace t

Stephen Mahon 0 Nov 30, 2021
NAT puncher for Wireguard mesh networking.

natpunch-go This is a NAT hole punching tool designed for creating Wireguard mesh networks. It was inspired by Tailscale and informed by this example.

Malcolm Seyd 123 Dec 12, 2022
Zero Trust Network Communication Sentinel provides peer-to-peer, multi-protocol, automatic networking, cross-CDN and other features for network communication.

Thank you for your interest in ZASentinel ZASentinel helps organizations improve information security by providing a better and simpler way to protect

ZTALAB 8 Nov 1, 2022
A pluggable backend API that enforces the Event Sourcing Pattern for persisting & broadcasting application state changes

A pluggable "Application State Gateway" that enforces the Event Sourcing Pattern for securely persisting & broadcasting application state changes

null 29 Nov 1, 2022
Walrus 🕑 Real-time event streaming platform built on top of gRPC streams

Walrus ?? Real-time event streaming platform built on top of gRPC streams Table of Contents About the project Built With How it works Getting Started

Matheus Mosca 14 Sep 24, 2022
Bell is the simplest event system written in Go (Golang) which is based on the execution of handlers independent of the main channel.

Bell Bell is the simplest event system written in Go (Golang) which is based on the execution of handlers independent of the main channel. Written in

NUT.Tech 29 Nov 17, 2022
Event Source for Direktiv and Knative

Event Source for Direktiv and Knative This repository contains ContainerSources for Knative Eventing. The following sources are available: Direktiv (I

direktiv.io 2 Mar 13, 2022
Goket (Golang Keyboard Event Tree) is a proof-of-concept code for using keyboard events trees for performing operations.

Goket Goket (Golang Keyboard Event Tree) is a proof-of-concept code for using keyboard events trees for performing operations. Its main goal is to all

Wojciech Kocjan 0 Jan 3, 2023
Event driven modular status-bar for dwm; written in Go & uses Unix sockets for signaling.

dwmstat A simple event-driven modular status-bar for dwm. It is written in Go & uses Unix sockets for signaling. The status bar is conceptualized as a

Navaz Alani 1 Dec 25, 2021
Events - Event Manager - Nodejs like

events Event Manager - Nodejs like Please take a look at the TESTS, for further comprehension. Example package main import ( "errors" "fmt" "log"

Digital Circle 0 Dec 31, 2021