tile38/vendor/github.com/tidwall/resp/README.md

194 lines
5.4 KiB
Markdown

**This project has been archived. If you are looking for a high-performance Redis server for Go, please checkout [Redcon](https://github.com/tidwall/redcon). It's much faster than this implementation and can handle pipelining.**
RESP
====
[![GoDoc](https://godoc.org/github.com/tidwall/resp?status.svg)](https://godoc.org/github.com/tidwall/resp)
RESP is a [Go](http://golang.org/) library that provides a reader, writer, and server implementation for the [Redis RESP Protocol](http://redis.io/topics/protocol).
RESP is short for **REdis Serialization Protocol**.
While the protocol was designed specifically for Redis, it can be used for other client-server software projects.
The RESP protocol has the advantages of being human readable and with performance of a binary protocol.
Features
--------
- [Reader](#reader) and [Writer](#writer) types for streaming RESP values from files, networks, or byte streams.
- [Server Implementation](#server) for creating your own RESP server. [Clients](#clients) use the same tools and libraries as Redis.
- [Append-only File](#append-only-file) type for persisting RESP values to disk.
Installation
------------
Install resp using the "go get" command:
go get github.com/tidwall/resp
The Go distribution is Resp's only dependency.
Documentation
-------------
- [API Reference](http://godoc.org/github.com/tidwall/resp)
Server
------
A Redis clone that implements the SET and GET commands.
- You can interact using the Redis CLI (redis-cli). http://redis.io/download
- Or, use the telnet by typing in "telnet localhost 6380" and type in "set key value" and "get key".
- Or, use a client library such as http://github.com/garyburd/redigo
- The "QUIT" command will close the connection.
```go
package main
import (
"errors"
"log"
"sync"
"github.com/tidwall/resp"
)
func main() {
var mu sync.RWMutex
kvs := make(map[string]string)
s := resp.NewServer()
s.HandleFunc("set", func(conn *resp.Conn, args []resp.Value) bool {
if len(args) != 3 {
conn.WriteError(errors.New("ERR wrong number of arguments for 'set' command"))
} else {
mu.Lock()
kvs[args[1].String()] = args[2].String()
mu.Unlock()
conn.WriteSimpleString("OK")
}
return true
})
s.HandleFunc("get", func(conn *resp.Conn, args []resp.Value) bool {
if len(args) != 2 {
conn.WriteError(errors.New("ERR wrong number of arguments for 'get' command"))
} else {
mu.RLock()
s, ok := kvs[args[1].String()]
mu.RUnlock()
if !ok {
conn.WriteNull()
} else {
conn.WriteString(s)
}
}
return true
})
if err := s.ListenAndServe(":6379"); err != nil {
log.Fatal(err)
}
}
```
Reader
------
The resp Reader type allows for an application to read raw RESP values from a file, network, or byte stream.
```go
raw := "*3\r\n$3\r\nset\r\n$6\r\nleader\r\n$7\r\nCharlie\r\n"
raw += "*3\r\n$3\r\nset\r\n$8\r\nfollower\r\n$6\r\nSkyler\r\n"
rd := resp.NewReader(bytes.NewBufferString(raw))
for {
v, _, err := rd.ReadValue()
if err == io.EOF {
break
}
if err != nil {
log.Fatal(err)
}
fmt.Printf("Read %s\n", v.Type())
if v.Type() == Array {
for i, v := range v.Array() {
fmt.Printf(" #%d %s, value: '%s'\n", i, v.Type(), v)
}
}
}
// Output:
// Read Array
// #0 BulkString, value: 'set'
// #1 BulkString, value: 'leader'
// #2 BulkString, value: 'Charlie'
// Read Array
// #0 BulkString, value: 'set'
// #1 BulkString, value: 'follower'
// #2 BulkString, value: 'Skyler'
```
Writer
------
The resp Writer type allows for an application to write raw RESP values to a file, network, or byte stream.
```go
var buf bytes.Buffer
wr := resp.NewWriter(&buf)
wr.WriteArray([]resp.Value{resp.StringValue("set"), resp.StringValue("leader"), resp.StringValue("Charlie")})
wr.WriteArray([]resp.Value{resp.StringValue("set"), resp.StringValue("follower"), resp.StringValue("Skyler")})
fmt.Printf("%s", buf.String())
// Output:
// *3\r\n$3\r\nset\r\n$6\r\nleader\r\n$7\r\nCharlie\r\n
// *3\r\n$3\r\nset\r\n$8\r\nfollower\r\n$6\r\nSkyler\r\n
```
Append-Only File
----------------
An append only file (AOF) allows your application to persist values to disk. It's very easy to use, and includes the same level of durablilty and binary format as [Redis AOF Persistence](http://redis.io/topics/persistence).
Check out the [AOF documentation](https://godoc.org/github.com/tidwall/resp#AOF) for more information
```go
// create and fill an appendonly file
aof, err := resp.OpenAOF("appendonly.aof")
if err != nil {
log.Fatal(err)
}
// append a couple values and close the file
aof.Append(resp.MultiBulkValue("set", "leader", "Charlie"))
aof.Append(resp.MultiBulkValue("set", "follower", "Skyler"))
aof.Close()
// reopen and scan all values
aof, err = resp.OpenAOF("appendonly.aof")
if err != nil {
log.Fatal(err)
}
defer aof.Close()
aof.Scan(func(v Value) {
fmt.Printf("%s\n", v.String())
})
// Output:
// [set leader Charlie]
// [set follower Skyler]
}
```
Clients
-------
There are bunches of [RESP Clients](http://redis.io/clients). Most any client that supports Redis will support this implementation.
Contact
-------
Josh Baker [@tidwall](http://twitter.com/tidwall)
License
-------
Tile38 source code is available under the MIT [License](/LICENSE).