RedHub
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 establishedonClosed: Called when a connection is closedhandler: 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 optionsrh: 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\n$3\r\nGET\r\n$3\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 integerAppendString(b []byte, s string) []byte- Append simple stringAppendBulk(b []byte, bulk []byte) []byte- Append bulk bytesAppendBulkString(b []byte, bulk string) []byte- Append bulk stringAppendArray(b []byte, n int) []byte- Append array headerAppendError(b []byte, s string) []byte- Append errorAppendNull(b []byte) []byte- Append null valueAppendOK(b []byte) []byte- Append OK responseAppendAny(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\n$3\r\nSET\r\n$3\r\nkey1\r\n$5\r\nvalue1\r\n*2\r\n$3\r\nSET\r\n$3\r\nkey2\r\n$5\r\nvalue2\r\n*2\r\n$3\r\nGET\r\n$3\r\nkey1\r\n' | nc localhost 6379
Multi-Protocol Support
RedHub supports three protocol types:
- RESP (Redis) - Standard Redis protocol (commands starting with
*) - Tile38 Native - Native Tile38 protocol (commands starting with
$) - 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
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
- Enable Multi-core: Always enable
Multicore: truein production - Tune Buffer Sizes: Adjust
ReadBufferCap,SocketRecvBuffer, andSocketSendBufferbased on your workload - Choose Load Balancing: Use appropriate load balancing strategy (RoundRobin, LeastConnections, etc.)
- Avoid Blocking: Never block in event handlers - use async operations
- Reuse Buffers: Use buffer pools for temporary allocations
Thread Safety
- Shared Data: Always protect shared data with appropriate synchronization (mutexes)
- Connection Context: Use
Conn.SetContext()for per-connection data (thread-safe) - Event Loop Handlers: Handlers execute in event loop threads - avoid heavy computations
Error Handling
- Protocol Errors: Return proper RESP error messages using
resp.AppendError() - Connection Errors: Log errors in
onClosedhandler - Graceful Shutdown: Handle server shutdown properly
Contributing
We welcome contributions! Please follow these guidelines:
- Fork the repository
- Create a new branch from main/master
- Make your changes with tests
- Ensure all tests pass:
go test ./... - Commit with DCO sign-off:
git commit -s -m "message" - Push to your fork
- 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
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
- GitHub Issues: https://github.com/IceFireDB/redhub/issues
- Discussions: https://github.com/IceFireDB/redhub/discussions