evio/examples/redis-server/main.go

180 lines
4.4 KiB
Go
Raw Normal View History

2017-07-04 06:39:18 +03:00
package main
import (
"flag"
"fmt"
"io"
"log"
"os"
"strings"
"github.com/tidwall/redcon"
"github.com/tidwall/shiny"
)
func main() {
var port int
var appendonly string
flag.IntVar(&port, "port", 6380, "server port")
flag.StringVar(&appendonly, "appendonly", "no", "use appendonly file (yes or no)")
flag.Parse()
var resp []byte
var aofw []byte
var args [][]byte
var shutdown bool
var started bool
var bufs = make(map[int][]byte)
var keys = make(map[string]string)
var f *os.File
if appendonly == "yes" {
var err error
f, err = os.OpenFile("appendonly.aof", os.O_CREATE|os.O_RDWR, 0600)
if err != nil {
log.Fatal(err)
}
defer f.Close()
rd := redcon.NewReader(f)
for {
cmd, err := rd.ReadCommand()
if err == io.EOF {
break
} else if err != nil {
log.Fatal(err)
}
switch strings.ToUpper(string(cmd.Args[0])) {
default:
log.Fatal("bad aof")
case "SET":
keys[string(cmd.Args[1])] = string(cmd.Args[2])
}
}
}
log.Fatal(shiny.Serve("tcp", fmt.Sprintf(":%d", port),
func(id int, data []byte, ctx interface{}) (send []byte, keepopen bool) {
if shutdown {
return nil, false
}
buf := bufs[id]
if len(buf) > 0 {
data = append(buf, data...)
}
keepopen = true
resp = resp[:0]
aofw = aofw[:0]
var complete bool
var err error
for {
complete, args, _, data, err = redcon.ReadNextCommand(data, args[:0])
if err != nil {
keepopen = false
resp = redcon.AppendError(resp, err.Error())
break
}
if !complete {
break
}
switch strings.ToUpper(string(args[0])) {
default:
resp = redcon.AppendError(resp, fmt.Sprintf("ERR unknown command '%s'", args[0]))
case "PING":
if len(args) > 2 {
resp = redcon.AppendError(resp, fmt.Sprintf("ERR wrong number of arguments for '%s' command", args[0]))
continue
} else if len(args) == 1 {
resp = redcon.AppendString(resp, "PONG")
} else {
resp = redcon.AppendBulk(resp, args[1])
}
case "QUIT":
keepopen = false
resp = redcon.AppendOK(resp)
case "SHUTDOWN":
shutdown = true
keepopen = false
resp = redcon.AppendOK(resp)
case "SET":
if len(args) != 3 {
resp = redcon.AppendError(resp, fmt.Sprintf("ERR wrong number of arguments for '%s' command", args[0]))
continue
}
keys[string(args[1])] = string(args[2])
resp = redcon.AppendOK(resp)
if appendonly == "yes" {
// create the append only entry
aofw = redcon.AppendArray(aofw, 3)
aofw = redcon.AppendBulkString(aofw, "SET")
aofw = redcon.AppendBulk(aofw, args[1])
aofw = redcon.AppendBulk(aofw, args[2])
}
case "GET":
if len(args) != 2 {
resp = redcon.AppendError(resp, fmt.Sprintf("ERR wrong number of arguments for '%s' command", args[0]))
continue
}
val, ok := keys[string(args[1])]
if !ok {
resp = redcon.AppendNull(resp)
} else {
resp = redcon.AppendBulkString(resp, val)
}
case "DEL":
if len(args) < 2 {
resp = redcon.AppendError(resp, fmt.Sprintf("ERR wrong number of arguments for '%s' command", args[0]))
continue
}
var n int64
for i := 1; i < len(args); i++ {
if _, ok := keys[string(args[i])]; ok {
delete(keys, string(args[i]))
n++
}
}
resp = redcon.AppendInt(resp, n)
}
}
if len(data) > 0 {
bufs[id] = append(buf[:0], data...)
} else if len(buf) > 0 {
bufs[id] = buf[:0]
}
if len(aofw) > 0 {
if _, err := f.Write(aofw); err != nil {
log.Fatal(err)
}
}
return resp, keepopen
},
// accept - a new client socket has opened
func(id int, addr string, wake func(), ctx interface{}) (send []byte, keepopen bool) {
if shutdown {
return nil, false
}
// create a new socket context here
return nil, true
},
// closed - a client socket has closed
func(id int, err error, ctx interface{}) {
// teardown the socket context here
delete(bufs, id)
},
// ticker - a ticker that fires between 1 and 1/20 of a second
// depending on the traffic.
func(ctx interface{}) (keepserveropen bool) {
if shutdown {
// do server teardown here
return false
}
if !started {
fmt.Printf("redis(ish) server started on port %d\n", port)
started = true
}
// perform various non-socket-io related operation here
return true
},
// an optional user-defined context
nil))
}