-
Notifications
You must be signed in to change notification settings - Fork 1
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implement Redis server #16
Open
nemati21
wants to merge
3
commits into
main
Choose a base branch
from
Implement-Redis-server
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+321
−6
Open
Changes from 1 commit
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
package config | ||
|
||
import ( | ||
"github.com/knadh/koanf/parsers/yaml" | ||
"github.com/knadh/koanf/providers/file" | ||
"github.com/knadh/koanf/providers/structs" | ||
"go.uber.org/zap" | ||
|
||
"github.com/snapp-incubator/proksi/internal/logging" | ||
) | ||
|
||
var ( | ||
// Redis is the config for ProksiRedis | ||
Redis *RedisConfig | ||
) | ||
|
||
var defaultRedis = RedisConfig{ | ||
MainFrontend: Frontend{ | ||
Bind: "127.0.0.1:6381", | ||
}, | ||
TestFrontend: Frontend{ | ||
Bind: "127.0.0.1:6382", | ||
}, | ||
Backend: Backend{ | ||
Address: "127.0.0.1:6380", | ||
}, | ||
} | ||
|
||
// RedisConfig represent config of the ProksiRedis. | ||
type RedisConfig struct { | ||
MainFrontend Frontend `koanf:"main_frontend"` | ||
TestFrontend Frontend `koanf:"test_frontend"` | ||
Backend Backend `koanf:"backend"` | ||
} | ||
|
||
type Frontend struct { | ||
Bind string `koanf:"bind"` | ||
} | ||
|
||
type Backend struct { | ||
Address string `koanf:"address"` | ||
} | ||
|
||
// LoadRedis function will load the file located in path and return the parsed config. This function will panic on errors | ||
func LoadRedis(path string) *RedisConfig { | ||
// Load default config in the beginning | ||
err := k.Load(structs.Provider(defaultRedis, "koanf"), nil) | ||
if err != nil { | ||
logging.L.Fatal("error in loading the default config", zap.Error(err)) | ||
} | ||
|
||
// Load YAML config and merge into the previously loaded config. | ||
err = k.Load(file.Provider(path), yaml.Parser()) | ||
if err != nil { | ||
logging.L.Fatal("error in loading the config file", zap.Error(err)) | ||
} | ||
|
||
var c RedisConfig | ||
err = k.Unmarshal("", &c) | ||
if err != nil { | ||
logging.L.Fatal("error in unmarshalling the config file", zap.Error(err)) | ||
} | ||
|
||
Redis = &c | ||
return &c | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
main_frontend: | ||
bind: "127.0.0.1:6381" | ||
|
||
test_frontend: | ||
bind: "127.0.0.1:6382" | ||
|
||
backend: | ||
address: "127.0.0.1:6380" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
package redis | ||
|
||
import ( | ||
"sync" | ||
|
||
"github.com/tidwall/redcon" | ||
) | ||
|
||
type Proxy struct { | ||
mux sync.RWMutex | ||
items map[string][]byte | ||
cache map[string][]byte | ||
errSig chan bool | ||
} | ||
|
||
func NewProxy() *Proxy { | ||
return &Proxy{ | ||
items: make(map[string][]byte), | ||
cache: make(map[string][]byte), | ||
errSig: make(chan bool), | ||
} | ||
} | ||
|
||
func (p *Proxy) ping(conn redcon.Conn, cmd redcon.Command) { | ||
p.cache = map[string][]byte{string(cmd.Args[0]): []byte("PONG")} | ||
conn.WriteString("PONG") | ||
} | ||
|
||
func (p *Proxy) set(conn redcon.Conn, cmd redcon.Command) { | ||
if len(cmd.Args) < 3 { | ||
conn.WriteError("ERR wrong number of arguments for '" + string(cmd.Args[0]) + "' command") | ||
return | ||
} | ||
|
||
p.mux.Lock() | ||
p.items[string(cmd.Args[1])] = cmd.Args[2] | ||
p.mux.Unlock() | ||
|
||
p.cache = map[string][]byte{string(cmd.Args[0]): []byte("OK")} | ||
|
||
conn.WriteString("OK") | ||
} | ||
|
||
func (p *Proxy) get(conn redcon.Conn, cmd redcon.Command) { | ||
if len(cmd.Args) != 2 { | ||
conn.WriteError("ERR wrong number of arguments for '" + string(cmd.Args[0]) + "' command") | ||
return | ||
} | ||
|
||
p.mux.RLock() | ||
value, found := p.items[string(cmd.Args[1])] | ||
p.mux.RUnlock() | ||
|
||
if !found { | ||
p.cache = map[string][]byte{string(cmd.Args[0]): []byte("")} | ||
conn.WriteNull() | ||
} | ||
p.cache = map[string][]byte{string(cmd.Args[0]): value} | ||
conn.WriteBulk(value) | ||
} | ||
|
||
func (p *Proxy) delete(conn redcon.Conn, cmd redcon.Command) { | ||
if len(cmd.Args) != 2 { | ||
conn.WriteError("ERR wrong number of arguments for '" + string(cmd.Args[0]) + "' command") | ||
return | ||
} | ||
|
||
p.mux.RLock() | ||
_, found := p.items[string(cmd.Args[1])] | ||
delete(p.items, string(cmd.Args[1])) | ||
p.mux.Unlock() | ||
|
||
if !found { | ||
p.cache = map[string][]byte{string(cmd.Args[0]): []byte("0")} | ||
conn.WriteInt(0) | ||
} | ||
p.cache = map[string][]byte{string(cmd.Args[0]): []byte("1")} | ||
conn.WriteInt(1) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,114 @@ | ||
package redis | ||
|
||
import ( | ||
"flag" | ||
"log" | ||
|
||
"github.com/tidwall/redcon" | ||
|
||
"github.com/snapp-incubator/proksi/internal/config" | ||
) | ||
|
||
type ServerType string | ||
|
||
const ( | ||
Main ServerType = "main" | ||
Test ServerType = "test" | ||
) | ||
|
||
type FunctionType string | ||
|
||
var ( | ||
help bool // Indicates whether to show the help or not | ||
configPath string // Path of config file | ||
) | ||
|
||
func init() { | ||
flag.BoolVar(&help, "help", false, "Show help") | ||
flag.StringVar(&configPath, "config", "", "The path of config file") | ||
|
||
// Parse the terminal flags | ||
flag.Parse() | ||
} | ||
|
||
func main() { | ||
// Usage Demo | ||
if help { | ||
flag.Usage() | ||
return | ||
} | ||
|
||
c := config.LoadRedis(configPath) | ||
|
||
h := NewProxy() | ||
|
||
errSig := make(chan bool) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You should implement a graceful shutdown mechanism. |
||
|
||
log.Printf("proxing from %v to %v\n", c.MainFrontend.Bind, c.Backend.Address) | ||
|
||
go h.serve(Main, c.Backend.Address, errSig) | ||
go h.serve(Test, c.MainFrontend.Bind, errSig) | ||
<-h.errSig | ||
} | ||
|
||
// serve implements the Redis server. | ||
func (p *Proxy) serve(serverType ServerType, address string, errSig chan bool) { | ||
var err error | ||
|
||
for { | ||
if serverType == Main { | ||
log.Printf("started server at %s", address) | ||
|
||
err = redcon.ListenAndServe(address, p.mainHandler(), accept, p.closed()) | ||
if err != nil { | ||
log.Print(err) | ||
errSig <- true | ||
return | ||
} | ||
} else { | ||
log.Printf("started server at %s", address) | ||
|
||
err = redcon.ListenAndServe(address, p.testHandler(), accept, p.closed()) | ||
if err != nil { | ||
log.Print(err) | ||
errSig <- true | ||
return | ||
} | ||
} | ||
} | ||
} | ||
|
||
// mainHandler is an RESP handler for the main Redis server that responds to command and fills a cache. | ||
func (p *Proxy) mainHandler() func(conn redcon.Conn, cmd redcon.Command) { | ||
mux := redcon.NewServeMux() | ||
mux.HandleFunc("ping", p.ping) | ||
mux.HandleFunc("set", p.set) | ||
mux.HandleFunc("get", p.get) | ||
mux.HandleFunc("del", p.delete) | ||
|
||
return mux.ServeRESP | ||
} | ||
|
||
// testHandler is an RESP handler for the test Redis server that lookup the cache and sends responses. | ||
func (p *Proxy) testHandler() func(conn redcon.Conn, cmd redcon.Command) { | ||
return func(conn redcon.Conn, cmd redcon.Command) { | ||
result, found := p.cache[string(cmd.Args[0])] | ||
if found { | ||
conn.WriteString(string(result)) | ||
} | ||
} | ||
} | ||
|
||
// accept is called to accept or deny the connection. | ||
func accept(conn redcon.Conn) bool { | ||
log.Printf("The connection between %s and %s haas been accepted", conn.NetConn().LocalAddr().String(), conn.RemoteAddr()) | ||
return true | ||
} | ||
|
||
// closed is called when the connection has been closed. | ||
func (p *Proxy) closed() func(conn redcon.Conn, err error) { | ||
return func(conn redcon.Conn, err error) { | ||
log.Printf("The connection between %s, %s has been closed, err: %v", conn.NetConn().LocalAddr().String(), conn.RemoteAddr(), err) | ||
<-p.errSig | ||
} | ||
} |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's no reason to implement the Redis logic. It's just a proxy server. Please remove them.