redcon

package module
v0.9.3 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 8, 2021 License: MIT Imports: 8 Imported by: 3

README

REDCON
Build Status GoDoc

Fast Redis compatible server framework for Go

Redcon is a custom Redis server framework for Go that is fast and simple to use. The reason for this library it to give an efficient server front-end for the BuntDB and Tile38 projects.

Features

  • Create a Fast custom Redis compatible server in Go
  • Simple interface. One function ListenAndServe and two types Conn & Command
  • Support for pipelining and telnet commands
  • Works with Redis clients such as redigo, redis-py, node_redis, and jedis

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

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)
	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)
				}
			}
		},
		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)
	}
}

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.

Documentation

Overview

Package redcon implements a Redis compatible server framework

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func ListenAndServe

func ListenAndServe(addr string,
	handler func(conn Conn, cmd Command),
	accept func(conn Conn) bool,
	closed func(conn Conn, err error),
) error

ListenAndServe creates a new server and binds to addr configured on "tcp" network net.

func ListenAndServeNetwork

func ListenAndServeNetwork(
	net, laddr string,
	handler func(conn Conn, cmd Command),
	accept func(conn Conn) bool,
	closed func(conn Conn, err error),
) error

ListenAndServe creates a new server and binds to addr. The network net must be a stream-oriented network: "tcp", "tcp4", "tcp6", "unix" or "unixpacket"

Types

type Command

type Command struct {
	// Raw is a encoded RESP message.
	Raw []byte
	// Args is a series of arguments that make up the command.
	Args [][]byte
}

Command represent a command

func Parse

func Parse(raw []byte) (Command, error)

Parse parses a raw RESP message and returns a command.

type Conn

type Conn interface {
	// RemoteAddr returns the remote address of the client connection.
	RemoteAddr() string
	// Close closes the connection.
	Close() error
	// WriteError writes an error to the client.
	WriteError(msg string)
	// WriteString writes a string to the client.
	WriteString(str string)
	// WriteBulk writes bulk bytes to the client.
	WriteBulk(bulk []byte)
	// WriteBulkString writes a bulk string to the client.
	WriteBulkString(bulk string)
	// WriteInt writes an integer to the client.
	WriteInt(num int)
	// WriteInt64 writes a 64-but signed integer to the client.
	WriteInt64(num int64)
	// WriteArray writes an array header. You must then write addtional
	// sub-responses to the client to complete the response.
	// For example to write two strings:
	//
	//   c.WriteArray(2)
	//   c.WriteBulk("item 1")
	//   c.WriteBulk("item 2")
	WriteArray(count int)
	// WriteNull writes a null to the client
	WriteNull()
	// WriteRaw writes raw data to the client.
	WriteRaw(data []byte)
	// Context returns a user-defined context
	Context() interface{}
	// SetContext sets a user-defined context
	SetContext(v interface{})
	// SetReadBuffer updates the buffer read size for the connection
	SetReadBuffer(bytes int)
	// Detach return a connection that is detached from the server.
	// Useful for operations like PubSub.
	//
	//   dconn := conn.Detach()
	//   go func(){
	//       defer dconn.Close()
	//       cmd, err := dconn.ReadCommand()
	//       if err != nil{
	//           fmt.Printf("read failed: %v\n", err)
	//	         return
	//       }
	//       fmt.Printf("received command: %v", cmd)
	//       hconn.WriteString("OK")
	//       if err := dconn.Flush(); err != nil{
	//           fmt.Printf("write failed: %v\n", err)
	//	         return
	//       }
	//   }()
	Detach() DetachedConn
	// ReadPipeline returns all commands in current pipeline, if any
	// The commands are removed from the pipeline.
	ReadPipeline() []Command
	// PeekPipeline returns all commands in current pipeline, if any.
	// The commands remain in the pipeline.
	PeekPipeline() []Command
	// NetConn returns the base net.Conn connection
	NetConn() net.Conn
	// Flush flushes any writes to the network.
	Flush() error
}

Conn represents a client connection

type DetachedConn

type DetachedConn interface {
	// Conn is the original connection
	Conn
	// ReadCommand reads the next client command.
	ReadCommand() (Command, error)
}

DetachedConn represents a connection that is detached from the server

type Reader

type Reader struct {
	// contains filtered or unexported fields
}

Reader represent a reader for RESP or telnet commands.

func NewReader

func NewReader(rd io.Reader) *Reader

NewReader returns a command reader which will read RESP or telnet commands.

func NewReaderSize added in v0.9.0

func NewReaderSize(rd io.Reader, s int) *Reader

func (*Reader) ReadCommand

func (rd *Reader) ReadCommand() (Command, error)

ReadCommand reads the next command.

func (*Reader) ReadCommands

func (rd *Reader) ReadCommands() ([]Command, error)

func (*Reader) Reset

func (rd *Reader) Reset(r io.Reader)

type Server

type Server struct {
	// contains filtered or unexported fields
}

Server defines a server for clients for managing client connections.

func NewServer

func NewServer(addr string,
	handler func(conn Conn, cmd Command),
	accept func(conn Conn) bool,
	closed func(conn Conn, err error),
) *Server

NewServer returns a new Redcon server configured on "tcp" network net.

func NewServerNetwork

func NewServerNetwork(
	net, laddr string,
	handler func(conn Conn, cmd Command),
	accept func(conn Conn) bool,
	closed func(conn Conn, err error),
) *Server

NewServerNetworkType returns a new Redcon server. The network net must be a stream-oriented network: "tcp", "tcp4", "tcp6", "unix" or "unixpacket"

func (*Server) Close

func (s *Server) Close() error

Close stops listening on the TCP address. Already Accepted connections will be closed.

func (*Server) ListenAndServe

func (s *Server) ListenAndServe() error

ListenAndServe serves incoming connections.

func (*Server) ListenServeAndSignal

func (s *Server) ListenServeAndSignal(signal chan error) error

ListenServeAndSignal serves incoming connections and passes nil or error when listening. signal can be nil.

func (*Server) SetIdleClose added in v0.9.2

func (s *Server) SetIdleClose(dur time.Duration)

SetIdleClose will automatically close idle connections after the specified duration. Use zero to disable this feature.

type Writer

type Writer struct {
	// contains filtered or unexported fields
}

Writer allows for writing RESP messages.

func BaseWriter

func BaseWriter(c Conn) *Writer

BaseWriter returns the underlying connection writer, if any

func NewWriter

func NewWriter(wr io.Writer) *Writer

NewWriter creates a new RESP writer.

func (*Writer) Buffer

func (w *Writer) Buffer() []byte

Buffer returns the unflushed buffer. This is a copy so changes to the resulting []byte will not affect the writer.

func (*Writer) Flush

func (w *Writer) Flush() error

Flush writes all unflushed Write* calls to the underlying writer.

func (*Writer) SetBuffer

func (w *Writer) SetBuffer(raw []byte)

SetBuffer replaces the unflushed buffer with new bytes.

func (*Writer) WriteArray

func (w *Writer) WriteArray(count int)

WriteArray writes an array header. You must then write addtional sub-responses to the client to complete the response. For example to write two strings:

c.WriteArray(2)
c.WriteBulk("item 1")
c.WriteBulk("item 2")

func (*Writer) WriteBulk

func (w *Writer) WriteBulk(bulk []byte)

WriteBulk writes bulk bytes to the client.

func (*Writer) WriteBulkString

func (w *Writer) WriteBulkString(bulk string)

WriteBulkString writes a bulk string to the client.

func (*Writer) WriteError

func (w *Writer) WriteError(msg string)

WriteError writes an error to the client.

func (*Writer) WriteInt

func (w *Writer) WriteInt(num int)

WriteInt writes an integer to the client.

func (*Writer) WriteInt64

func (w *Writer) WriteInt64(num int64)

WriteInt64 writes a 64-bit signed integer to the client.

func (*Writer) WriteNull

func (w *Writer) WriteNull()

WriteNull writes a null to the client

func (*Writer) WriteRaw

func (w *Writer) WriteRaw(data []byte)

WriteRaw writes raw data to the client.

func (*Writer) WriteString

func (w *Writer) WriteString(msg string)

WriteString writes a string to the client.

Directories

Path Synopsis

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL