commit e2f2d0a622043f4d5254bde29e530d3ecf4dc86a Author: Enric Lluelles Date: Fri May 20 21:03:18 2016 +0200 First draft, it's working already diff --git a/README.md b/README.md new file mode 100644 index 0000000..842619a --- /dev/null +++ b/README.md @@ -0,0 +1,13 @@ +redis-sentinel-proxy +==================== + +Small command utility that: + +* Given a redis sentinel server listening on `SENTINEL_PORT`, keeps asking it for the address of a master named `NAME` + +* Proxies all tcp requests that it receives on `PORT` to that master + + +Usage: + +`./redis-sentinel-proxy -listen IP:PORT -sentinel :SENTINEL_PORT -master NAME` diff --git a/main.go b/main.go new file mode 100644 index 0000000..bcfc166 --- /dev/null +++ b/main.go @@ -0,0 +1,102 @@ +package main + +import ( + "flag" + "fmt" + "io" + "log" + "net" + "strings" + "time" +) + +var ( + masterAddr *net.TCPAddr + raddr *net.TCPAddr + saddr *net.TCPAddr + + localAddr = flag.String("listen", ":9999", "local address") + sentinelAddr = flag.String("sentinel", ":26379", "remote address") + masterName = flag.String("master", "mymaster", "name of the master redis node") +) + +func main() { + flag.Parse() + + laddr, err := net.ResolveTCPAddr("tcp", *localAddr) + if err != nil { + log.Fatal("Failed to resolve local address: %s", err) + } + saddr, err = net.ResolveTCPAddr("tcp", *sentinelAddr) + if err != nil { + log.Fatal("Failed to resolve sentinel address: %s", err) + } + + go master() + + listener, err := net.ListenTCP("tcp", laddr) + if err != nil { + log.Fatal(err) + } + + for { + conn, err := listener.AcceptTCP() + if err != nil { + log.Println(err) + continue + } + + go proxy(conn, masterAddr) + } +} + +func master() { + var err error + for { + masterAddr, err = getMasterAddr(saddr, *masterName) + if err != nil { + log.Fatal(err) + } + time.Sleep(1 * time.Second) + } +} + +func pipe(r io.Reader, w io.WriteCloser) { + io.Copy(w, r) + w.Close() +} + +func proxy(local io.ReadWriteCloser, remoteAddr *net.TCPAddr) { + remote, err := net.DialTCP("tcp", nil, remoteAddr) + if err != nil { + log.Println(err) + local.Close() + return + } + go pipe(local, remote) + go pipe(remote, local) +} + +func getMasterAddr(sentinelAddress *net.TCPAddr, masterName string) (*net.TCPAddr, error) { + conn, err := net.DialTCP("tcp", nil, sentinelAddress) + if err != nil { + log.Fatal("Error connecting to sentinel: %s", err) + } + + defer conn.Close() + + conn.Write([]byte(fmt.Sprintf("sentinel get-master-addr-by-name %s\n", masterName))) + + b := make([]byte, 256) + _, err = conn.Read(b) + if err != nil { + log.Fatal(err) + } + + parts := strings.Split(string(b), "\r\n") + + //getting the string address for the master node + stringaddr := fmt.Sprintf("%s:%s", parts[2], parts[4]) + addr, err := net.ResolveTCPAddr("tcp", stringaddr) + return addr, err +}