Skip to content

Navigation Menu

Sign in
Sign up

Repository files navigation

REDHUB

RedHub

GoDoc Reference FOSSA Status Go Report Card License

RedHub is a high-performance RESP (Redis Serialization Protocol) server framework built in Go. It leverages the RawEpoll model via the gnet library to achieve ultra-high throughput with multi-threaded support while maintaining low CPU resource consumption.

Features

  • Ultra High Performance - Exceeds Redis single-threaded and multi-threaded implementations in benchmarks
  • Fully Multi-threaded - Native support for multiple CPU cores with efficient event loop distribution
  • Low Resource Consumption - Optimized memory usage and CPU efficiency
  • Full RESP Protocol Support - Compatible with Redis protocol (RESP2)
  • Multi-Protocol Support - Supports RESP, Tile38 native, and Telnet protocols
  • Easy to Use - Create Redis-compatible servers with minimal code
  • Production Ready - Robust error handling, connection management, and extensibility

Architecture

RedHub implements an event-driven architecture based on the gnet framework:

┌─────────────────────────────────────────────────────────────┐
│ Client Connections │
└─────────────────────────────┬───────────────────────────────┘
 │
 ▼
┌─────────────────────────────────────────────────────────────┐
│ Event Loops (gnet) │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ Event Loop 1│ │ Event Loop 2│ │ Event Loop N│ │
│ │ (Thread 1) │ │ (Thread 2) │ │ (Thread N) │ │
│ └──────┬──────┘ └──────┬──────┘ └──────┬──────┘ │
│ │ │ │ │
│ └─────────────────┼─────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────┐ │
│ │ RedHub Core │ │
│ │ Handler │ │
│ └──────┬───────┘ │
└───────────────────────────┼──────────────────────────────────┘
 │
 ▼
 ┌─────────────────┐
 │ Application │
 │ Logic & Storage │
 └─────────────────┘

Threading Model

  • Single-core mode: All connections handled by a single event loop
  • Multi-core mode: Multiple event loops distribute connections using configurable load balancing strategies
  • Connection Buffering: Each connection maintains its own buffer for command accumulation
  • Thread Safety: Uses RWMutex for connection map synchronization

Installation

go get -u github.com/IceFireDB/redhub

Quick Start

Here's a simple example showing how to create a Redis-compatible server with SET, GET, DEL, PING, and QUIT commands:

Example Code

package main
import (
 "log"
 "strings"
 "sync"
 "github.com/IceFireDB/redhub"
 "github.com/IceFireDB/redhub/pkg/resp"
)
func main() {
 var mu sync.RWMutex
 var items = make(map[string][]byte)
 // Create a new RedHub instance
 rh := redhub.NewRedHub(
 // OnOpen: Called when a new connection is established
 func(c *redhub.Conn) (out []byte, action redhub.Action) {
 // Initialize connection-specific data here
 return nil, redhub.None
 },
 // OnClose: Called when a connection is closed
 func(c *redhub.Conn, err error) (action redhub.Action) {
 // Clean up connection-specific data here
 return redhub.None
 },
 // Handler: Called for each parsed command
 func(cmd resp.Command, out []byte) ([]byte, redhub.Action) {
 // Get command name (case-insensitive)
 cmdName := strings.ToLower(string(cmd.Args[0]))
 switch cmdName {
 case "set":
 // SET key value
 if len(cmd.Args) != 3 {
 return resp.AppendError(out, 
 "ERR wrong number of arguments for 'set' command"), redhub.None
 }
 mu.Lock()
 items[string(cmd.Args[1])] = cmd.Args[2]
 mu.Unlock()
 return resp.AppendString(out, "OK"), redhub.None
 case "get":
 // GET key
 if len(cmd.Args) != 2 {
 return resp.AppendError(out, 
 "ERR wrong number of arguments for 'get' command"), redhub.None
 }
 mu.RLock()
 val, ok := items[string(cmd.Args[1])]
 mu.RUnlock()
 if !ok {
 return resp.AppendNull(out), redhub.None
 }
 return resp.AppendBulk(out, val), redhub.None
 case "del":
 // DEL key
 if len(cmd.Args) != 2 {
 return resp.AppendError(out, 
 "ERR wrong number of arguments for 'del' command"), redhub.None
 }
 mu.Lock()
 _, ok := items[string(cmd.Args[1])]
 delete(items, string(cmd.Args[1]))
 mu.Unlock()
 if !ok {
 return resp.AppendInt(out, 0), redhub.None
 }
 return resp.AppendInt(out, 1), redhub.None
 case "ping":
 // PING
 return resp.AppendString(out, "PONG"), redhub.None
 case "quit":
 // QUIT
 return resp.AppendString(out, "OK"), redhub.Close
 default:
 // Unknown command
 return resp.AppendError(out, 
 "ERR unknown command '"+string(cmd.Args[0])+"'"), redhub.None
 }
 },
 )
 // Start the server
 err := redhub.ListenAndServe("tcp://127.0.0.1:6379", redhub.Options{
 Multicore: true, // Enable multi-core support
 }, rh)
 if err != nil {
 log.Fatal(err)
 }
}

Run the Example

# Navigate to the example directory
cd example/memory_kv
# Run the server
go run server.go
# In another terminal, test with redis-cli
redis-cli -p 6379
# Or test with redis-benchmark
redis-benchmark -h 127.0.0.1 -p 6379 -n 1000000 -t set,get -c 512 -P 1024 -q

Configuration

RedHub provides various configuration options through the Options struct:

type Options struct {
 Multicore bool // Enable multi-core support (default: false)
 LockOSThread bool // Lock OS thread (default: false)
 ReadBufferCap int // Read buffer capacity (default: 64KB)
 LB gnet.LoadBalancing // Load balancing strategy (default: RoundRobin)
 NumEventLoop int // Number of event loops (default: runtime.NumCPU())
 ReusePort bool // Enable port reuse (default: false)
 Ticker bool // Enable ticker (default: false)
 TCPKeepAlive time.Duration // TCP keep-alive interval
 TCPKeepCount int // TCP keep-alive count
 TCPKeepInterval time.Duration // TCP keep-alive interval
 TCPNoDelay gnet.TCPSocketOpt // TCP no-delay option
 SocketRecvBuffer int // Socket receive buffer size
 SocketSendBuffer int // Socket send buffer size
 EdgeTriggeredIO bool // Edge-triggered I/O (default: false)
}

Example Configuration

options := redhub.Options{
 Multicore: true, // Enable multi-core
 NumEventLoop: 8, // Use 8 event loops
 ReadBufferCap: 64 * 1024, // 64KB read buffer
 SocketRecvBuffer: 128 * 1024, // 128KB socket receive buffer
 SocketSendBuffer: 128 * 1024, // 128KB socket send buffer
 TCPKeepAlive: 30 * time.Second, // 30s keep-alive
 LB: gnet.LeastConnections, // Load balancing strategy
}

API Reference

Core Types

Action

Action represents the action to take after an event handler completes.

const (
 None // No action
 Close // Close the connection
 Shutdown // Shutdown the server
)

RedHub

RedHub is the main server structure that manages connections and command processing.

Conn

Conn wraps a gnet.Conn and provides additional functionality for connection management.

Command

Command represents a parsed RESP command with raw bytes and arguments.

type Command struct {
 Raw []byte // Raw RESP message
 Args [][]byte // Parsed arguments
}

Main Functions

NewRedHub

Creates a new RedHub instance with the specified event handlers.

func NewRedHub(
 onOpened func(c *Conn) (out []byte, action Action),
 onClosed func(c *Conn, err error) (action Action),
 handler func(cmd resp.Command, out []byte) ([]byte, Action),
) *RedHub

Parameters:

  • onOpened: Called when a new connection is established
  • onClosed: Called when a connection is closed
  • handler: Called for each parsed command

ListenAndServe

Starts the RedHub server with the specified address and options.

func ListenAndServe(addr string, options Options, rh *RedHub) error

Parameters:

  • addr: Server address in format "tcp://host:port"
  • options: Server configuration options
  • rh: RedHub instance

RESP Protocol Package

The resp package provides comprehensive support for the Redis Serialization Protocol (RESP).

RESP Types

const (
 Integer = ':' // Integers (e.g., :1000\r\n)
 String = '+' // Simple strings (e.g., +OK\r\n)
 Bulk = '$' // Bulk strings (e.g., 6ドル\r\nfoobar\r\n)
 Array = '*' // Arrays (e.g., *2\r\n3ドル\r\nGET\r\n3ドル\r\nkey\r\n)
 Error = '-' // Errors (e.g., -ERR unknown command\r\n)
)

RESP Serialization Functions

The resp package provides functions for serializing various Go types to RESP format:

  • AppendInt(b []byte, n int64) []byte - Append integer
  • AppendString(b []byte, s string) []byte - Append simple string
  • AppendBulk(b []byte, bulk []byte) []byte - Append bulk bytes
  • AppendBulkString(b []byte, bulk string) []byte - Append bulk string
  • AppendArray(b []byte, n int) []byte - Append array header
  • AppendError(b []byte, s string) []byte - Append error
  • AppendNull(b []byte) []byte - Append null value
  • AppendOK(b []byte) []byte - Append OK response
  • AppendAny(b []byte, v interface{}) []byte - Append any Go type

Example: Building Responses

var out []byte
// Simple string
out = resp.AppendString(out, "OK")
// Bulk string
out = resp.AppendBulkString(out, "Hello World")
// Integer
out = resp.AppendInt(out, 42)
// Array
out = resp.AppendArray(out, 3)
out = resp.AppendBulkString(out, "item1")
out = resp.AppendBulkString(out, "item2")
out = resp.AppendBulkString(out, "item3")
// Error
out = resp.AppendError(out, "ERR something went wrong")
// Null value
out = resp.AppendNull(out)
// Any type
out = resp.AppendAny(out, map[string]interface{}{
 "name": "Redis",
 "version": 7.0,
 "features": []string{"persistence", "replication"},
})

Advanced Usage

Connection Context

Store connection-specific data using Conn.SetContext():

type ConnectionData struct {
 Authenticated bool
 Database int
 ClientID string
}
onOpened := func(c *redhub.Conn) (out []byte, action redhub.Action) {
 c.SetContext(&ConnectionData{
 Authenticated: false,
 Database: 0,
 ClientID: generateID(),
 })
 return nil, redhub.None
}
onClosed := func(c *redhub.Conn, err error) (action redhub.Action) {
 ctx := c.Context().(*ConnectionData)
 // Cleanup connection data
 return redhub.None
}

Command Pipelining

RedHub naturally supports command pipelining (sending multiple commands in a single network packet):

# Client sends multiple commands in one request
echo -e '*2\r\n3ドル\r\nSET\r\n3ドル\r\nkey1\r\n5ドル\r\nvalue1\r\n*2\r\n3ドル\r\nSET\r\n3ドル\r\nkey2\r\n5ドル\r\nvalue2\r\n*2\r\n3ドル\r\nGET\r\n3ドル\r\nkey1\r\n' | nc localhost 6379

Multi-Protocol Support

RedHub supports three protocol types:

  1. RESP (Redis) - Standard Redis protocol (commands starting with *)
  2. Tile38 Native - Native Tile38 protocol (commands starting with $)
  3. Telnet - Plain text commands

Performance Benchmarks

Test Environment

OS: Debian Buster 10.6 64bit
CPU: 8 CPU cores
Memory: 64.0 GiB
Go: go1.16.5 linux/amd64

Benchmark Results

Implementation SET (req/sec) GET (req/sec)
Redis 5.0.3 (single-threaded) 2,306,060 3,096,742
Redis 6.2.5 (single-threaded) 2,076,325 2,652,801
Redis 6.2.5 (multi-threaded) 1,944,692 2,375,184
RedCon (multi-threaded) 2,332,742 14,654,162
RedHub (multi-threaded) 4,087,305 16,490,765

Benchmark Command

redis-benchmark -h 127.0.0.1 -p 6379 -n 50000000 -t set,get -c 512 -P 1024 -q

REDHUB Benchmarks

REDHUB Benchmarks

Testing

Run All Tests

go test ./...

Run Tests with Coverage

go test -cover ./...

Run Tests with Verbose Output

go test -v ./...

Run Specific Package Tests

go test ./pkg/resp/...

Run Specific Test

go test -run TestNewRedHub .

Best Practices

Performance Optimization

  1. Enable Multi-core: Always enable Multicore: true in production
  2. Tune Buffer Sizes: Adjust ReadBufferCap, SocketRecvBuffer, and SocketSendBuffer based on your workload
  3. Choose Load Balancing: Use appropriate load balancing strategy (RoundRobin, LeastConnections, etc.)
  4. Avoid Blocking: Never block in event handlers - use async operations
  5. Reuse Buffers: Use buffer pools for temporary allocations

Thread Safety

  1. Shared Data: Always protect shared data with appropriate synchronization (mutexes)
  2. Connection Context: Use Conn.SetContext() for per-connection data (thread-safe)
  3. Event Loop Handlers: Handlers execute in event loop threads - avoid heavy computations

Error Handling

  1. Protocol Errors: Return proper RESP error messages using resp.AppendError()
  2. Connection Errors: Log errors in onClosed handler
  3. Graceful Shutdown: Handle server shutdown properly

Contributing

We welcome contributions! Please follow these guidelines:

  1. Fork the repository
  2. Create a new branch from main/master
  3. Make your changes with tests
  4. Ensure all tests pass: go test ./...
  5. Commit with DCO sign-off: git commit -s -m "message"
  6. Push to your fork
  7. Create a pull request

Development Setup

# Clone the repository
git clone https://github.com/IceFireDB/redhub.git
cd redhub
# Install dependencies
go mod download
# Run tests
go test ./...
# Run the example
go run example/memory_kv/server.go

License

FOSSA Status

This project is licensed under the MIT License - see the LICENSE file for details.

Disclaimer

When you use this software, you agree and acknowledge that the author, maintainer, and contributor of this software are not responsible for any risks, costs, or problems you encounter. If you find a software defect or bug, please submit a patch to help improve it!

Related Projects

  • IceFireDB - A distributed database based on RedHub
  • gnet - High-performance event-loop networking framework

Documentation

Support

Acknowledgments

About

High-performance Redis-Server multi-threaded framework, based on rawepoll model.

Topics

Resources

Code of conduct

Contributing

Stars

80 stars

Watchers

4 watching

Forks

Releases

Packages

Used by

Contributors

Languages

AltStyle によって変換されたページ (->オリジナル) /