redis TCP concurrency

This commit is contained in:
Meutel 2017-07-09 18:46:32 +02:00
parent d9437be5a2
commit 72c34f3568
2 changed files with 83 additions and 45 deletions

View File

@ -10,6 +10,17 @@ import (
"strings" "strings"
) )
type RedisCmd struct {
cmd string
key string
val string
}
type CommandExec struct {
*RedisCmd
Result chan string
}
func getCommand(reader io.Reader) (string, error) { func getCommand(reader io.Reader) (string, error) {
scanner := bufio.NewScanner(reader) scanner := bufio.NewScanner(reader)
for scanner.Scan() { for scanner.Scan() {
@ -32,8 +43,8 @@ func isCommand(str string) bool {
} }
} }
func parseCmd(line string) (string, string, string, error) { func parseCmd(line string) (*RedisCmd, error) {
cmd, key, val := "", "", "" redisCmd := new(RedisCmd)
var err error var err error
parts := strings.Split(line, " ") parts := strings.Split(line, " ")
if len(parts) != 2 && len(parts) != 3 { if len(parts) != 2 && len(parts) != 3 {
@ -43,23 +54,23 @@ func parseCmd(line string) (string, string, string, error) {
switch i { switch i {
case 0: case 0:
if isCommand(part) { if isCommand(part) {
cmd = part redisCmd.cmd = part
} else { } else {
err = errors.New("Unknown command") err = errors.New("Unknown command")
break break
} }
case 1: case 1:
key = part redisCmd.key = part
case 2: case 2:
if cmd == "SET" { if redisCmd.cmd == "SET" {
val = part redisCmd.val = part
} else { } else {
err = errors.New("Invalid number of arguments") err = errors.New("Invalid number of arguments")
} }
} }
} }
} }
return cmd, key, val, err return redisCmd, err
} }
func respond(writer io.Writer, ret string) { func respond(writer io.Writer, ret string) {
@ -78,39 +89,68 @@ func main() {
} }
defer ln.Close() defer ln.Close()
commands := make(chan CommandExec)
go func() {
for exec := range commands {
switch exec.cmd {
case "DEL":
log.Println("Deleting", exec.key)
delete(db, exec.key)
close(exec.Result)
case "GET":
log.Println("Getting", exec.key)
ret, ok := db[exec.key]
if ok {
log.Println("Value found", ret)
go func() {
exec.Result <- ret
}()
} else {
log.Println("Unknown key", exec.key)
close(exec.Result)
}
case "SET":
log.Println("Setting", exec.key)
db[exec.key] = exec.val
close(exec.Result)
}
}
}()
for { for {
conn, err := ln.Accept() conn, err := ln.Accept()
if err != nil { if err != nil {
panic(err) panic(err)
} }
results := make(chan string)
go func() { go func() {
line, err := getCommand(conn) ret, ok := <-results
if err != nil {
respond(conn, fmt.Sprint(err))
}
cmd, key, val, err := parseCmd(line)
if err != nil {
respond(conn, fmt.Sprint(err))
}
switch cmd {
case "DEL":
log.Println("Deleting", key)
delete(db, key)
case "GET":
log.Println("Getting", key)
ret, ok := db[key]
if ok { if ok {
log.Println("Value found", ret)
respond(conn, ret) respond(conn, ret)
} else {
log.Println("Unknown key", key)
}
case "SET":
log.Println("Setting", key)
db[key] = val
} }
defer conn.Close() defer conn.Close()
}() }()
go func() {
exec := new(CommandExec)
exec.Result = results
line, err := getCommand(conn)
if err != nil {
go func() {
exec.Result <- fmt.Sprint(err)
}()
}
cmd, err := parseCmd(line)
if err != nil {
go func() {
exec.Result <- fmt.Sprint(err)
}()
}
exec.RedisCmd = cmd
commands <- *exec
}()
} }
} }

View File

@ -47,20 +47,18 @@ func TestRespond(t *testing.T) {
type ParseCmdTest struct { type ParseCmdTest struct {
line string line string
fails bool fails bool
cmd string RedisCmd
key string
val string
} }
func TestParseCommand(t *testing.T) { func TestParseCommand(t *testing.T) {
tests := []ParseCmdTest{ tests := []ParseCmdTest{
{"test", true, "", "", ""}, {"test", true, RedisCmd{"", "", ""}},
{"test test", true, "", "", ""}, {"test test", true, RedisCmd{"", "", ""}},
{"SET 1 2 3", true, "", "", ""}, {"SET 1 2 3", true, RedisCmd{"", "", ""}},
{"GET 1 2", true, "", "", ""}, {"GET 1 2", true, RedisCmd{"", "", ""}},
} }
for _, test := range tests { for _, test := range tests {
c, k, v, e := parseCmd(test.line) c, e := parseCmd(test.line)
if test.fails { if test.fails {
if e == nil { if e == nil {
t.Log("should fail (" + test.line + ")") t.Log("should fail (" + test.line + ")")
@ -71,16 +69,16 @@ func TestParseCommand(t *testing.T) {
t.Log("should no fail", e) t.Log("should no fail", e)
t.Fail() t.Fail()
} }
if c != test.cmd { if c.cmd != test.cmd {
t.Log("Error command", c, test.cmd) t.Log("Error command", c.cmd, test.cmd)
t.Fail() t.Fail()
} }
if k != test.key { if c.key != test.key {
t.Log("Error key", k, test.key) t.Log("Error key", c.key, test.key)
t.Fail() t.Fail()
} }
if v != test.val { if c.val != test.val {
t.Log("Error value", v, test.val) t.Log("Error value", c.val, test.val)
t.Fail() t.Fail()
} }
} }