nbio

repository·master·Indexed 25 days ago

https://github.com/lesismal/nbio

A non-blocking I/O library for Go designed for massive connection scenarios. It reduces goroutine and object overhead to lower memory consumption and GC pressure in high-concurrency environments like gateways and proxies. Supports TCP, UDP, Unix Sockets, TLS, HTTP/HTTPS 1.x, and WebSockets across Linux (Epoll), BSD/MacOS (Kqueue), and Windows.

Tokens
5.3K
Snippets
5
Records
44
Agent score
33%

What's inside nbio

  1. Supported Protocols and Platforms

    master

    Supported Protocols

    • TCP/UDP/Unix Socket
    • TLS
    • HTTP/HTTPS 1.x
    • Websocket (Passes Autobahn Test Suite; OnOpen/OnMessage/OnClose order guaranteed)

    Supported Platforms

    • Linux: Epoll with LT/ET/ET+ONESHOT support (LT is default).
    • BSD (MacOS): Kqueue.
    • Windows: Based on standard net library (for debugging purposes only).
  2. Quick Start with nbio Engine

    master

    To get started with nbio, create a new engine using nbio.NewEngine with a nbio.Config. You can then register callbacks for connection lifecycle events (OnOpen, OnClose) and data handling (OnData). Finally, call engine.Start() to begin listening.

    package main
    
    import (
    	"log"
    
    	"github.com/lesismal/nbio"
    )
    
    func main() {
    	engine := nbio.NewEngine(nbio.Config{
    		Network:            "tcp", //"udp", "unix"
    		Addrs:              []string{":8888"},
    		MaxWriteBufferSize: 6 * 1024 * 1024,
    	})
    
    	// handle new connection
    	engine.OnOpen(func(c *nbio.Conn) {
    		log.Println("OnOpen:", c.RemoteAddr().String())
    	})
    	// handle connection closed
    	engine.OnClose(func(c *nbio.Conn, err error) {
    		log.Println("OnClose:", c.RemoteAddr().String(), err)
    	})
    	// handle data
    	engine.OnData(func(c *nbio.Conn, data []byte) {
    		c.Write(append([]byte{}, data...))
    	})
    
    	err := engine.Start()
    	if err != nil {
    		log.Fatalf("nbio.Start failed: %v\n", err)
    		return
    	}
    	defer engine.Stop()
    
    	<-make(chan int)
    }
  3. Configure nbio.Engine

    master

    The nbio.NewEngine function accepts a nbio.Config struct to define the server behavior. Key configuration fields include:

    • Network: The network type, supported values are "tcp", "udp", or "unix".
    • Addrs: A slice of strings representing the addresses to listen on (e.g., []string{":8888"}).
    • MaxWriteBufferSize: The maximum write buffer size in bytes.
    engine := nbio.NewEngine(nbio.Config{
    	Network:            "tcp",
    	Addrs:              []string{":8888"},
    	MaxWriteBufferSize: 6 * 1024 * 1024,
    })
  4. Configure IOMod for HTTP and WebSocket performance

    master

    NBIO provides different IOMod settings to control how connections are handled, allowing you to balance performance, CPU, and memory usage.

    • IOModNonBlocking: All connections are handled by the poller (default behavior).
    • IOModBlocking: All connections are handled by at least one goroutine. This can improve performance for low-concurrency services. For WebSockets, you can set Upgrader.BlockingModAsyncWrite=true to handle writing in a separate goroutine, which helps avoid Head-of-line blocking during broadcasting.
    • IOModMixed: Uses Engine.MaxBlockingOnline to balance modes. If the number of online connections is smaller than MaxBlockingOnline, new connections are handled via IOModBlocking. Otherwise, they are handled by the poller.
  5. Use WebSocket with a standard net/http server

    master

    You can integrate nbio's WebSocket implementation into a standard Go net/http server using the websocket.Upgrader. This allows you to use the high-performance NBIO WebSocket handling within a traditional HTTP server architecture.

    To implement this, you must:

    1. Create a websocket.Upgrader using websocket.NewUpgrader().
    2. Define event handlers for OnOpen, OnMessage, and OnClose.
    3. Call upgrader.Upgrade(w, r, nil) within your HTTP handler to upgrade the connection.
    package main
    
    import (
    	"fmt"
    	"net/http"
    
    	"github.com/lesismal/nbio/nbhttp/websocket"
    )
    
    var (
    	upgrader = newUpgrader()
    )
    
    func newUpgrader() *websocket.Upgrader {
    	u := websocket.NewUpgrader()
    	u.OnOpen(func(c *websocket.Conn) {
    		// echo
    		fmt.Println("OnOpen:", c.RemoteAddr().String())
    	})
    	u.OnMessage(func(c *websocket.Conn, messageType websocket.MessageType, data []byte) {
    		// echo
    		fmt.Println("OnMessage:", messageType, string(data))
    		c.WriteMessage(messageType, data)
    	})
    	u.OnClose(func(c *websocket.Conn, err error) {
    		fmt.Println("OnClose:", c.RemoteAddr().String(), err)
    	})
    	return u
    }
    
    func onWebsocket(w http.ResponseWriter, r *http.Request) {
    	conn, err := upgrader.Upgrade(w, r, nil)
    	if err != nil {
    		panic(err)
    	}
    	fmt.Println("Upgraded:", conn.RemoteAddr().String())
    }
    
    func main() {
    	mux := &http.ServeMux{}
    	mux.HandleFunc("/ws", onWebsocket)
    	server := http.Server{
    		Addr:    "localhost:8080",
    		Handler: mux,
    	}
    	fmt.Println("server exit:", server.ListenAndServe())
    }
  6. nbio Interface Capabilities

    master

    The nbio library provides several key interface features:

    • Implements a non-blocking net.Conn (except on Windows).
    • Supports SetDeadline, SetReadDeadline, and SetWriteDeadline.
    • Supports concurrent Write and Close operations for both nbio.Conn and nbio/nbhttp/websocket.Conn.
  7. Configure the nbio Engine

    master

    The Config struct defines the settings for an Engine instance. You can use it to set up network protocols, buffer sizes, poller counts, and custom listener functions. If Addrs is empty, the Engine defaults to client mode.

    Key configuration fields:

    • Name: Logging name (defaults to "NB").
    • Network: Listening protocol (e.g., NETWORK_TCP, NETWORK_UDP).
    • Addrs: List of listening addresses.
    • NPoller: Number of poller goroutines.
    • ReadBufferSize: Size of the read buffer (default 64k).
    • MaxWriteBufferSize: Limit for write buffer; if exceeded, the connection is closed.
    • AsyncReadInPoller: If true, the epoll goroutine only handles events and a separate pool handles reading. If false, the epoll goroutine handles both.
    • Listen: Custom listener function (e.g., for reuseport).
    • ListenUDP: Custom UDP listener function.
  8. Initialize a non-blocking HTTP server with NewServer

    master

    Use NewServer to create a new non-blocking HTTP server instance. You can optionally provide an http.Handler and a custom ServerExecutor (a function with the signature func(f func())) via variadic arguments.

    Arguments:

    • conf Config: The server configuration.
    • v ...interface{} (Optional):
      • v[0]: An http.Handler to handle requests.
      • v[1]: A func(f func()) to act as the ServerExecutor.
  9. Write data with Write and Writev

    master

    Write data to the connection using Write or Writev.

    Important Notes:

    • Non-blocking behavior: If the connection cannot write all data immediately, nbio will cache the remaining bytes in an internal writeList and attempt to flush them when the socket becomes writable.
    • UDP Limitation: Writev does not support UDP if more than one []byte is provided in the input slice.
    • Overflow: If the amount of cached data exceeds the configured MaxWriteBufferSize, Write will return an error.
  10. Initialize an NBIO Engine with NewEngine

    master
    Use NewEngine(conf Config) to create a new Engine instance with default configurations. The Config object allows you to specify the engine name, number of pollers (NPoller), read/write buffer sizes, and custom allocators. If certain fields are omitted, the engine applies sensible defaults (e.g., NPoller defaults to runtime.NumCPU() / 4).
  11. Read data asynchronously with AsyncRead

    master
    Call AsyncRead to trigger an asynchronous read operation. This is designed for non-blocking IO environments. Note that nbio uses an internal event loop to handle the actual reading and data dispatching.
  12. Manage user sessions on a Conn

    master
    You can attach arbitrary user data to a Conn using the Session() and SetSession() methods. This is useful for maintaining state (like authentication info) associated with a specific connection.