# README
Redis compatible server framework for Go
Features
- Create a Fast custom Redis compatible server in Go
- Simple interface. One function
ListenAndServe
and two typesConn
&Command
- Support for pipelining and telnet commands
- Works with Redis clients such as redigo, redis-py, node_redis, and jedis
- TLS Support
- Compatible pub/sub support
- Multithreaded
Installing
go get -u github.com/tidwall/redcon
Example
Here's a full example of a Redis clone that accepts:
- SET key value
- GET key
- DEL key
- PING
- QUIT
- PUBLISH channel message
- SUBSCRIBE channel
You can run this example from a terminal:
go run example/clone.go
package main
import (
"log"
"strings"
"sync"
"github.com/tidwall/redcon"
)
var addr = ":6380"
func main() {
var mu sync.RWMutex
var items = make(map[string][]byte)
var ps redcon.PubSub
go log.Printf("started server at %s", addr)
err := redcon.ListenAndServe(addr,
func(conn redcon.Conn, cmd redcon.Command) {
switch strings.ToLower(string(cmd.Args[0])) {
default:
conn.WriteError("ERR unknown command '" + string(cmd.Args[0]) + "'")
case "ping":
conn.WriteString("PONG")
case "quit":
conn.WriteString("OK")
conn.Close()
case "set":
if len(cmd.Args) != 3 {
conn.WriteError("ERR wrong number of arguments for '" + string(cmd.Args[0]) + "' command")
return
}
mu.Lock()
items[string(cmd.Args[1])] = cmd.Args[2]
mu.Unlock()
conn.WriteString("OK")
case "get":
if len(cmd.Args) != 2 {
conn.WriteError("ERR wrong number of arguments for '" + string(cmd.Args[0]) + "' command")
return
}
mu.RLock()
val, ok := items[string(cmd.Args[1])]
mu.RUnlock()
if !ok {
conn.WriteNull()
} else {
conn.WriteBulk(val)
}
case "del":
if len(cmd.Args) != 2 {
conn.WriteError("ERR wrong number of arguments for '" + string(cmd.Args[0]) + "' command")
return
}
mu.Lock()
_, ok := items[string(cmd.Args[1])]
delete(items, string(cmd.Args[1]))
mu.Unlock()
if !ok {
conn.WriteInt(0)
} else {
conn.WriteInt(1)
}
case "publish":
if len(cmd.Args) != 3 {
conn.WriteError("ERR wrong number of arguments for '" + string(cmd.Args[0]) + "' command")
return
}
conn.WriteInt(ps.Publish(string(cmd.Args[1]), string(cmd.Args[2])))
case "subscribe", "psubscribe":
if len(cmd.Args) < 2 {
conn.WriteError("ERR wrong number of arguments for '" + string(cmd.Args[0]) + "' command")
return
}
command := strings.ToLower(string(cmd.Args[0]))
for i := 1; i < len(cmd.Args); i++ {
if command == "psubscribe" {
ps.Psubscribe(conn, string(cmd.Args[i]))
} else {
ps.Subscribe(conn, string(cmd.Args[i]))
}
}
}
},
func(conn redcon.Conn) bool {
// Use this function to accept or deny the connection.
// log.Printf("accept: %s", conn.RemoteAddr())
return true
},
func(conn redcon.Conn, err error) {
// This is called when the connection has been closed
// log.Printf("closed: %s, err: %v", conn.RemoteAddr(), err)
},
)
if err != nil {
log.Fatal(err)
}
}
TLS Example
Redcon has full TLS support through the ListenAndServeTLS
function.
The same example is also provided for serving Redcon over TLS.
go run example/tls/clone.go
Benchmarks
Redis: Single-threaded, no disk persistence.
$ redis-server --port 6379 --appendonly no
redis-benchmark -p 6379 -t set,get -n 10000000 -q -P 512 -c 512
SET: 941265.12 requests per second
GET: 1189909.50 requests per second
Redcon: Single-threaded, no disk persistence.
$ GOMAXPROCS=1 go run example/clone.go
redis-benchmark -p 6380 -t set,get -n 10000000 -q -P 512 -c 512
SET: 2018570.88 requests per second
GET: 2403846.25 requests per second
Redcon: Multi-threaded, no disk persistence.
$ GOMAXPROCS=0 go run example/clone.go
$ redis-benchmark -p 6380 -t set,get -n 10000000 -q -P 512 -c 512
SET: 1944390.38 requests per second
GET: 3993610.25 requests per second
Running on a MacBook Pro 15" 2.8 GHz Intel Core i7 using Go 1.7
Contact
Josh Baker @tidwall
License
Redcon source code is available under the MIT License.
# Functions
AppendAny appends any type to valid Redis type.
AppendArray appends a Redis protocol array to the input bytes.
AppendBulk appends a Redis protocol bulk byte slice to the input bytes.
AppendBulkFloat appends a float64, as bulk bytes.
AppendBulkInt appends an int64, as bulk bytes.
AppendBulkString appends a Redis protocol bulk string to the input bytes.
AppendBulkUint appends an uint64, as bulk bytes.
AppendError appends a Redis protocol error to the input bytes.
AppendInt appends a Redis protocol int64 to the input bytes.
AppendNull appends a Redis protocol null to the input bytes.
AppendOK appends a Redis protocol OK to the input bytes.
AppendString appends a Redis protocol string to the input bytes.
AppendTile38 appends a Tile38 message to the input bytes.
AppendUint appends a Redis protocol uint64 to the input bytes.
BaseWriter returns the underlying connection writer, if any.
ListenAndServe creates a new server and binds to addr configured on "tcp" network net.
ListenAndServeNetwork creates a new server and binds to addr.
ListenAndServeNetworkTLS creates a new TLS server and binds to addr.
ListenAndServeTLS creates a new TLS server and binds to addr configured on "tcp" network net.
NewReader returns a command reader which will read RESP or telnet commands.
NewServeMux allocates and returns a new ServeMux.
NewServer returns a new Redcon server configured on "tcp" network net.
NewServerNetwork returns a new Redcon server.
NewServerNetworkTLS returns a new TLS Redcon server.
NewServerTLS returns a new Redcon TLS server configured on "tcp" network net.
NewWriter creates a new RESP writer.
Parse parses a raw RESP message and returns a command.
ReadNextCommand reads the next command from the provided packet.
ReadNextRESP returns the next resp in b and returns the number of bytes the took up the result.
Serve creates a new server and serves with the given net.Listener.
# Structs
Command represent a command.
PubSub is a Redis compatible pub/sub server.
Reader represent a reader for RESP or telnet commands.
RESP ...
ServeMux is an RESP command multiplexer.
Server defines a server for clients for managing client connections.
TLSServer defines a server for clients for managing client connections.
Writer allows for writing RESP messages.
# Interfaces
Conn represents a client connection.
DetachedConn represents a connection that is detached from the server.
A Handler responds to an RESP request.
Marshaler is the interface implemented by types that can marshal themselves into a Redis response type from an *Any call.
# Type aliases
The HandlerFunc type is an adapter to allow the use of ordinary functions as RESP handlers.
Kind is the kind of command.
SimpleInt is for representing a non-bulk representation of a int from an *Any call.
SimpleString is for representing a non-bulk representation of a string from an *Any call.
Type of RESP.