# Redeo
[![GoDoc](https://godoc.org/github.com/bsm/redeo?status.svg)](https://godoc.org/github.com/bsm/redeo)
[![Build Status](https://travis-ci.org/bsm/redeo.png?branch=master)](https://travis-ci.org/bsm/redeo)
[![Go Report Card](https://goreportcard.com/badge/github.com/bsm/redeo)](https://goreportcard.com/report/github.com/bsm/redeo)
[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0)
The high-performance Swiss Army Knife for building redis-protocol compatible servers/services.
## Parts
This repository is organised into multiple components:
* [root](./) package contains the framework for building redis-protocol compatible,
high-performance servers.
* [resp](./resp/) implements low-level primitives for dealing with
RESP (REdis Serialization Protocol), client and server-side. It
contains basic wrappers for readers and writers to read/write requests and
responses.
* [client](./client/) contains a minimalist pooled client.
For full documentation and examples, please see the individual packages and the
official API documentation: https://godoc.org/github.com/bsm/redeo.
## Examples
A simple server example with two commands:
```go
package main
import (
"net"
"github.com/bsm/redeo"
)
func main() {
// Init server and define handlers
srv := redeo.NewServer(nil)
srv.HandleFunc("ping", func(w resp.ResponseWriter, _ *resp.Command) {
w.AppendInlineString("PONG")
})
srv.HandleFunc("info", func(w resp.ResponseWriter, _ *resp.Command) {
w.AppendBulkString(srv.Info().String())
})
// Open a new listener
lis, err := net.Listen("tcp", ":9736")
if err != nil {
panic(err)
}
defer lis.Close()
// Start serving (blocking)
srv.Serve(lis)
}
```
More complex handlers:
```go
func main() {
mu := sync.RWMutex{}
myData := make(map[string]map[string]string)
srv := redeo.NewServer(nil)
// handle HSET
srv.HandleFunc("hset", func(w resp.ResponseWriter, c *resp.Command) {
// validate arguments
if c.ArgN() != 3 {
w.AppendError(redeo.WrongNumberOfArgs(c.Name))
return
}
// lock for write
mu.Lock()
defer mu.Unlock()
// fetch (find-or-create) key
hash, ok := myData[c.Arg(0).String()]
if !ok {
hash = make(map[string]string)
myData[c.Arg(0).String()] = hash
}
// check if field already exists
_, ok = hash[c.Arg(1).String()]
// set field
hash[c.Arg(1).String()] = c.Arg(2).String()
// respond
if ok {
w.AppendInt(0)
} else {
w.AppendInt(1)
}
})
// handle HGET
srv.HandleFunc("hget", func(w resp.ResponseWriter, c *resp.Command) {
if c.ArgN() != 2 {
w.AppendError(redeo.WrongNumberOfArgs(c.Name))
return
}
mu.RLock()
defer mu.RUnlock()
hash, ok := myData[c.Arg(0).String()]
if !ok {
w.AppendNil()
return
}
val, ok := hash[c.Arg(1).String()]
if !ok {
w.AppendNil()
return
}
w.AppendBulkString(val)
})
}
```