// ./slaveof-proxy -src localhost:6379 -dest remote:6379 -pullrate 400 -pushrate 400 func main() { runtime.GOMAXPROCS(4) src := flag.String("src", "", "master") dest := flag.String("dest", "", "slave") pullrate := flag.Int("pullrate", 400, "pull rate in Mbits/s") pushrate := flag.Int("pushrate", 400, "push rate in Mbits/s") buffer := flag.Int("buffer", 100, "buffer x10000 records") flag.Parse() if *pullrate < 100 { *pullrate = 100 } if *pushrate < 100 { *pushrate = 100 } if *buffer < 100 { *buffer = 100 } else if *buffer > 1000 { *buffer = 1000 } stdlog.Println("slaveof-proxy 1.0.3") stdlog.Printf("from [%s] to [%s]\n", *src, *dest) stdlog.Printf("pull [%d] buffer [%d]\n", *pullrate, *buffer) stdlog.Println("SYNC ...") client, err := NewClient(*src, *dest, *buffer) if err != nil { stdlog.Println("ERR", err) return } client.SetPullRate(*pullrate / 8 * 1024 * 1024) client.SetPushRate(*pushrate / 8 * 1024 * 1024) client.Sync() }
// S: SYNC UID [UID] PORT [PORT] SNAP [1/0] SEQ [-1/...] func (server *GoRedisServer) OnSYNC(session *Session, cmd *Command) (reply *Reply) { stdlog.Printf("[S %s] %s\n", session.RemoteAddr(), cmd) args := cmd.Args()[1:] if len(args) < 2 || len(args)%2 != 0 { session.Close() return } for i := 0; i < len(args); i += 2 { session.SetAttribute(string(args[i]), string(args[i+1])) } if !server.synclog.IsEnabled() { stdlog.Println("synclog enable") server.synclog.Enable() } // 使用从库端口代替socket端口,标识来源 h, _ := splitHostPort(session.RemoteAddr().String()) remoteHost := fmt.Sprintf("%s:%s", h, session.GetAttribute("PORT")) session.SetAttribute(S_HOST, remoteHost) session.SetAttribute(S_STATUS, REPL_WAIT) go func() { server.syncmgr.Put(remoteHost, session) err := server.doSync(session, cmd) if err != nil { stdlog.Println("sync ", err) } session.Close() server.syncmgr.Remove(remoteHost) }() return NOREPLY }
func (server *GoRedisServer) OnAOF(session *Session, cmd *Command) (reply *Reply) { defer func() { if v := recover(); v != nil { stdlog.Printf("aof panic %s\n", cmd) stdlog.Println(string(debug.Stack())) } }() onoff := strings.ToUpper(cmd.StringAtIndex(1)) if onoff == "YES" { if server.aofwriter != nil { return ErrorReply("aof already inited") } if !server.synclog.IsEnabled() { stdlog.Println("synclog enable") server.synclog.Enable() } go func() { err := server.aofStart() if err != nil { stdlog.Println("aof", err) } }() } else if onoff == "NO" { return server.onAOF_NO() } else { return ErrorReply("must be YES/NO") } return StatusReply("OK") }
func main() { opts := levigo.NewOptions() opts.SetCache(levigo.NewLRUCache(128 * 1024 * 1024)) opts.SetCompression(levigo.SnappyCompression) opts.SetBlockSize(32 * 1024) opts.SetWriteBufferSize(128 * 1024 * 1024) opts.SetMaxOpenFiles(100000) opts.SetCreateIfMissing(true) db, e1 := levigo.Open("/tmp/rocksdb0", opts) if e1 != nil { panic(e1) } stdlog.Println(db) batch := levigo.NewWriteBatch() batch.Put([]byte("name"), []byte("later")) wo := levigo.NewWriteOptions() db.Write(wo, batch) ro := levigo.NewReadOptions() value, err := db.Get(ro, []byte("name")) stdlog.Println(string(value), err) db.Close() }
func main() { srcptr := flag.String("src", "", "source host") destptr := flag.String("dest", "", "dest host") modePtr := flag.String("mode", "", "r/w/rw") flag.Parse() if len(*srcptr) == 0 || len(*destptr) == 0 { stdlog.Println("must set -src or -dest") return } desthost = *destptr mode = *modePtr if len(mode) == 0 { stdlog.Println("must set -mode [r|w|rw]") return } go runloop() r := redis_tool.NewMonitorReader(*srcptr) r.DidRecvCommand = recvCommand // bind err := r.Connect() if err != nil { panic(err) } }
// go run goredis-server.go -h localhost -p 1602 // go run goredis-server.go -procs 8 -p 17600 // go run goredis-server.go -slaveof localhost:1603 func main() { version := flag.Bool("v", false, "print goredis-server version") host := flag.String("h", "", "server host") port := flag.Int("p", 1602, "server port") slaveof := flag.String("slaveof", "", "replication") procs := flag.Int("procs", 8, "GOMAXPROCS") repair := flag.Bool("repair", false, "repaire rocksdb") flag.Parse() if *version { fmt.Println("goredis-server", goredis_server.VERSION) return } runtime.GOMAXPROCS(*procs) opt := goredis_server.NewOptions() opt.SetBind(fmt.Sprintf("%s:%d", *host, *port)) opt.SetDirectory(dbHome(*port)) if len(*slaveof) > 0 { h, p, e := splitHostPort(*slaveof) if e != nil { panic(e) } opt.SetSlaveOf(h, p) } // 重定向日志输出位置 redirectLogOutput(opt.Directory()) // repair if *repair { dbhome := opt.Directory() + "db0" finfo, e1 := os.Stat(dbhome) if os.IsNotExist(e1) || !finfo.IsDir() { stdlog.Println("db not exist") return } else { stdlog.Println("start repair", dbhome) levelredis.Repair(dbhome) stdlog.Println("repair finish") } return } stdlog.Println("========================================") server := goredis_server.NewGoRedisServer(opt) if err := server.Init(); err != nil { panic(err) } if err := server.Listen(); err != nil { panic(err) } }
// 初始化入口 func (server *GoRedisProxy) Init() (err error) { e1 := server.resetMaster(server.options.MasterHost) e2 := server.resetSlave(server.options.SlaveHost) if e1 != nil { stdlog.Println(e1) } if e2 != nil { stdlog.Println(e2) } return }
// 初始化leveldb func (server *GoRedisServer) initLevelDB() (err error) { opts := levelredis.NewOptions() cache := levelredis.NewLRUCache(128 * 1024 * 1024) opts.SetCache(cache) opts.SetCompression(levelredis.SnappyCompression) opts.SetBlockSize(8 * 1024) opts.SetMaxBackgroundCompactions(6) opts.SetWriteBufferSize(32 * 1024 * 1024) opts.SetMaxOpenFiles(100000) opts.SetCreateIfMissing(true) env := levelredis.NewDefaultEnv() env.SetBackgroundThreads(6) env.SetHighPriorityBackgroundThreads(2) opts.SetEnv(env) db, e1 := levelredis.Open(server.directory+"/db0", opts) if e1 != nil { return e1 } server.levelRedis = levelredis.NewLevelRedis(db, false) server.DeferClosing(func() { opts.Close() cache.Close() env.Close() stdlog.Println("db closed") }) return }
func (s *SlaveClient) Sync() (err error) { session := s.src if err = session.WriteCommand(NewCommand([]byte("SYNC"))); err != nil { return } rdbsaved := false for { var c byte if c, err = session.PeekByte(); err != nil { break } if !rdbsaved && c == '$' { if err = s.recvRdb(); err != nil { break } rdbsaved = true } else if c == '\n' { if _, err = session.ReadByte(); err != nil { break } stdlog.Println("waiting ...") } else { var cmd *Command if cmd, err = session.ReadCommand(); err != nil { break } s.counters.Get("in").Incr(1) s.buffer <- cmd } } return }
// 从主库获取数据 func (server *GoRedisServer) OnSLAVEOF(session *Session, cmd *Command) (reply *Reply) { // 保障不会奔溃 defer func() { if v := recover(); v != nil { stdlog.Printf("[%s] slaveof panic %s\n", session.RemoteAddr(), cmd) stdlog.Println(string(debug.Stack())) } }() arg1, arg2 := cmd.StringAtIndex(1), cmd.StringAtIndex(2) // SLAVEOF NO ONE if strings.ToUpper(arg1) == "NO" && strings.ToUpper(arg2) == "ONE" { return server.onSlaveOfNoOne(session, cmd) } // connect to master hostPort := arg1 + ":" + arg2 conn, err := net.Dial("tcp", hostPort) if err != nil { return ErrorReply(err) } // check exists remoteHost := conn.RemoteAddr().String() if server.slavemgr.Contains(remoteHost) { return ErrorReply("connection exists") } masterSession := NewSession(conn) isgoredis, version, err := redisInfo(masterSession) if err != nil { return ErrorReply(err) } var client ISlaveClient if isgoredis { slavelog.Printf("[M %s] SLAVEOF %s GoRedis:%s\n", remoteHost, remoteHost, version) if client, err = NewSlaveClientV2(server, masterSession); err != nil { return ErrorReply(err) } } else { slavelog.Printf("[M %s] SLAVEOF %s Redis:%s\n", remoteHost, remoteHost, version) if client, err = NewSlaveClient(server, masterSession); err != nil { return ErrorReply(err) } } // async go func() { client.Session().SetAttribute(S_STATUS, REPL_WAIT) server.slavemgr.Put(remoteHost, client) err := client.Sync() if err != nil { slavelog.Printf("[M %s] sync broken %s\n", remoteHost, err) } client.Close() server.slavemgr.Remove(remoteHost) }() return StatusReply("OK") }
// 初始化主从日志 func (server *GoRedisServer) initSyncLog() error { opts := levelredis.NewOptions() cache := levelredis.NewLRUCache(32 * 1024 * 1024) opts.SetCache(cache) opts.SetCompression(levelredis.SnappyCompression) opts.SetBlockSize(4 * 1024) opts.SetMaxBackgroundCompactions(2) opts.SetWriteBufferSize(32 * 1024 * 1024) opts.SetMaxOpenFiles(100000) opts.SetCreateIfMissing(true) env := levelredis.NewDefaultEnv() env.SetBackgroundThreads(2) env.SetHighPriorityBackgroundThreads(1) opts.SetEnv(env) db, e1 := levelredis.Open(server.directory+"/synclog", opts) if e1 != nil { return e1 } ldb := levelredis.NewLevelRedis(db, false) server.synclog = NewSyncLog(ldb, "sync") server.DeferClosing(func() { opts.Close() cache.Close() env.Close() stdlog.Println("synclog closed") }) return nil }
func (server *GoRedisServer) Init() (err error) { server.initSignalNotify() stdlog.Println("server init, version", VERSION, "...") err = server.initLevelDB() if err != nil { return } err = server.initSyncLog() if err != nil { return } server.config = NewConfig(server.levelRedis, PREFIX+"config:") // monitor server.initCommandMonitor(server.directory + "/cmd.log") server.initCommandCounterLog("string", []string{"GET", "SET", "MGET", "MSET", "INCR", "DECR", "INCRBY", "DECRBY"}) server.initCommandCounterLog("hash", []string{"HGETALL", "HGET", "HSET", "HDEL", "HMGET", "HMSET", "HINCRBY", "HLEN"}) server.initCommandCounterLog("set", []string{"SADD", "SCARD", "SISMEMBER", "SMEMBERS", "SREM"}) server.initCommandCounterLog("list", []string{"LPUSH", "RPUSH", "LPOP", "RPOP", "LINDEX", "LLEN", "LRANGE", "LTRIM"}) server.initCommandCounterLog("zset", []string{"ZADD", "ZCARD", "ZSCORE", "ZINCRBY", "ZRANGE", "ZRANGEBYSCORE", "ZRANK", "ZREM", "ZREMRANGEBYRANK", "ZREMRANGEBYSCORE", "ZREVRANGE", "ZREVRANGEBYSCORE", "ZREVRANK"}) server.initSeqLog(server.directory + "/seq.log") server.initLeveldbIOLog(server.directory + "/leveldb.io.log") server.initLeveldbStatsLog(server.directory + "/leveldb.stats.log") server.initExecLog(server.directory + "/exec.time.log") server.initSlowlog(server.directory + "/slow.log") stdlog.Printf("init uid %s\n", server.UID()) server.initSlaveOf() return }
func (server *GoRedisServer) onAOF_NO() (reply *Reply) { if server.aofwriter == nil { return ErrorReply("aof not inited") } server.aofwriter.Close() stdlog.Println("aof closed") return StatusReply("OK") }
// 包含w表示可写入,包容rr表示主从均可以读 // mode=r, 从库提供读,写操作返回错误 // mode=rr, 主从均提供读,写操作返回错误 // mode=rw, 主库提供写,从库提供读 // mode=rrw,主库提供写,主从均提供读 func (server *GoRedisProxy) resetMode(mode string) (err error) { server.Suspend() defer server.Resume() stdlog.Println("CONFIG mode", mode) server.options.Mode = mode return }
// ./slaveof-proxy -src localhost:6379 -dest remote:6379 -pullrate 400 -pushrate 400 func main() { runtime.GOMAXPROCS(4) src := flag.String("src", "", "master") dest := flag.String("dest", "", "slave") pullrate := flag.Int("pullrate", 400, "pull rate in Mbits/s") pushrate := flag.Int("pushrate", 400, "push rate in Mbits/s") buffer := flag.Int("buffer", 100, "buffer x10000 records") dbpath := flag.String("dbpath", "/tmp", "rdb path") flag.Parse() if *pullrate < 100 { *pullrate = 100 } if *pushrate < 100 { *pushrate = 100 } if *buffer < 100 { *buffer = 100 } else if *buffer > 1000 { *buffer = 1000 } stdlog.Println("slaveof-proxy 1.0.4") if len(*src) == 0 || len(*dest) == 0 { stdlog.Println("Usage: ./slaveof-proxy -src master:port -dest slave:6379 -pullrate 400 -pushrate 400 -buffer 100 -dbpath /tmp") return } stdlog.Printf("from [%s] to [%s]\n", *src, *dest) stdlog.Printf("pull [%d] buffer [%d]\n", *pullrate, *buffer) stdlog.Println("SYNC ...") client, err := NewClient(*src, *dest, *buffer) if err != nil { stdlog.Println("ERR", err) return } client.SetPullRate(*pullrate / 8 * 1024 * 1024) client.SetPushRate(*pushrate / 8 * 1024 * 1024) client.SetDbPath(*dbpath) err = client.Sync() if err != nil { panic(err) } }
func redirectToGoRedis(cmd *Command) (ok bool) { pool := GetRedisPool(desthost) conn := pool.Get() defer conn.Close() args := make([]interface{}, 0, len(cmd.Args)-1) for i := 1; i < len(cmd.Args); i++ { args = append(args, cmd.Args[i]) } _, err := conn.Do(cmd.StringAtIndex(0), args...) if err != nil { // io.EOF or "connection refused" stdlog.Println("ERR", len(buffer), total, cmd, err) return false } total++ stdlog.Println(len(buffer), total, cmd) return true }
// 处理退出事件 func (server *GoRedisServer) initSignalNotify() { server.sigs = make(chan os.Signal, 1) signal.Notify(server.sigs, syscall.SIGTERM) go func() { sig := <-server.sigs stdlog.Println("recv signal:", sig) server.Close() }() }
func (s *SlaveClient) readAllReply() { for { _, err := s.Dest().ReadByte() if err != nil { s.dest = nil s.destwriter = nil stdlog.Println("ReadReply ERR", err) time.Sleep(time.Millisecond * 1000) } } }
func main() { src := flag.String("src", "", "source host") dest := flag.String("dest", "", "dest host") mode := flag.String("mode", "", "r/w/rw") flag.Parse() if len(*src) == 0 || len(*dest) == 0 { stdlog.Println("must set -src or -dest") return } if len(*mode) == 0 { stdlog.Println("must set -mode [r|w|rw]") return } monitor := shardredis.NewMonitorRedirect(*src, *dest, *mode) if err := monitor.Start(); err != nil { panic(err) } }
// 关闭服务 func (server *GoRedisServer) Close() { server.closing = true // 标记退出 server.Suspend() // 挂起全部传入数据 time.Sleep(time.Millisecond * 2000) // 休息一下,Suspend瞬间可能还有数据库写入 server.levelRedis.Close() server.levelRedis = nil // 防止调用 server.synclog.Close() server.synclog = nil // 防止调用 for e := server.closingFunc.Front(); e != nil; e = e.Next() { fn, ok := e.Value.(func()) if ok { fn() } else { stdlog.Println("closing func err", e.Value) } } time.Sleep(time.Millisecond * 2000) // 休息一下 stdlog.Println("bye") os.Exit(0) }
func (server *GoRedisProxy) resetMaster(host string) (err error) { server.Suspend() defer server.Resume() stdlog.Println("CONFIG master", host) if server.master != nil { server.master.Close() } server.options.MasterHost = host server.master, err = NewRemoteSession(server.options.MasterHost, server.options.PoolSize) return }
func (server *GoRedisProxy) resetSlave(host string) (err error) { server.Suspend() defer server.Resume() stdlog.Println("CONFIG slave", host) if server.slave != nil { server.slave.Close() } server.options.SlaveHost = host server.slave, err = NewRemoteSession(server.options.SlaveHost, server.options.PoolSize) return }
// go run redis-proxy.go -p 1603 -master localhost:6379 -slave localhost:6389 func main() { runtime.GOMAXPROCS(8) // options opt := goredis_proxy.NewOptions() // flags version := flag.Bool("v", false, "print goredis-proxy version") flag.StringVar(&opt.Host, "h", "", "server host") flag.IntVar(&opt.Port, "p", 1602, "server port") flag.StringVar(&opt.MasterHost, "master", "", "master") flag.StringVar(&opt.SlaveHost, "slave", "", "slave") flag.StringVar(&opt.Mode, "mode", "rrw", "r/rr/rw/rrw, default rrw") flag.IntVar(&opt.PoolSize, "poolsize", 100, "pool for remote server") flag.Parse() if *version { fmt.Println("redis-proxy ", goredis_proxy.VERSION) return } if len(opt.MasterHost) == 0 || len(opt.SlaveHost) == 0 { stdlog.Println("bad master/slave") return } stdlog.Println("redis-proxy ", goredis_proxy.VERSION) stdlog.Printf("master:[%s], slave:[%s], mode:[%s], poolsize:[%d]\n", opt.MasterHost, opt.SlaveHost, opt.Mode, opt.PoolSize) stdlog.Println("listen", opt.Addr()) // start server := goredis_proxy.NewProxy(opt) err := server.Init() if err != nil { panic(err) } err = server.Listen(opt.Addr()) if err != nil { panic(err) } }
func (s *SlaveClient) writeCommand(cmd *Command) { s.mu.Lock() defer s.mu.Unlock() for { _, err := s.Writer().Write(cmd.Bytes()) if err != nil { s.dest = nil s.destwriter = nil stdlog.Println("WRITE_ERR", err, cmd) time.Sleep(time.Millisecond * 1000) continue } break } }
func (s *SlaveClient) Dest() *Session { s.connmu.Lock() defer s.connmu.Unlock() if s.dest == nil { for { conn, err := net.Dial("tcp", s.desthost) if err != nil { stdlog.Println("CONN_ERR", err) time.Sleep(time.Millisecond * 1000) continue } s.dest = NewSession(conn) break } } return s.dest }
func (server *GoRedisServer) doSync(session *Session, cmd *Command) (err error) { // snapshot var nextseq int64 if session.GetAttribute("SEQ") != nil { nextseq, err = strconv.ParseInt(session.GetAttribute("SEQ").(string), 10, 64) if err != nil { return } } remoteHost := session.GetAttribute(S_HOST).(string) if session.GetAttribute("SNAP") != nil && session.GetAttribute("SNAP").(string) == "1" { session.SetAttribute(S_STATUS, REPL_SEND_BULK) if nextseq, err = server.sendSnapshot(session); err != nil { stdlog.Printf("[S %s] snap send broken (%s)\n", remoteHost, err) return } } if nextseq < 0 { nextseq = 0 } if nextseq < server.synclog.MinSeq() || nextseq > server.synclog.MaxSeq()+1 { stdlog.Printf("[S %s] seq %d not in (%d, %d), closed\n", remoteHost, nextseq, server.synclog.MinSeq(), server.synclog.MaxSeq()) return errors.New("bad seq range") } // 如果整个同步过程s stdlog.Println("sync online ...") session.SetAttribute(S_STATUS, REPL_ONLINE) // 发送日志数据 err = server.syncRunloop(session, nextseq) if err != nil { stdlog.Printf("[S %s] sync broken (%s)\n", remoteHost, err) } return }
// ServerHandler.SessionOpened() func (server *GoRedisServer) SessionOpened(session *Session) { server.counters.Get("connection").Incr(1) server.sessmgr.Put(session.RemoteAddr().String(), session) stdlog.Println("connection accepted from", session.RemoteAddr()) }
func (server *GoRedisServer) aofStart() (err error) { if server.aofwriter == nil { filename := server.opt.LogPath() + "/appendonly.aof" f, e := os.OpenFile(filename, os.O_WRONLY|os.O_TRUNC|os.O_CREATE, os.ModePerm) if e != nil { return e } server.aofwriter = NewAOFWriter(bufio.NewWriter(f)) defer func() { f.Close() server.aofwriter.Close() server.aofwriter = nil }() stdlog.Println("aof inited") } server.Suspend() snap := server.levelRedis.Snapshot() snapclosed := false // 避免关闭两次 defer func() { if !snapclosed { snap.Close() } }() lastseq := server.synclog.MaxSeq() server.Resume() snap.KeyEnumerate([]byte(""), levelredis.IterForward, func(i int, key, keytype, value []byte, quit *bool) { // stdlog.Println(i, string(key), string(keytype)) if server.aofwriter.IsClosed() { *quit = true return } switch string(keytype) { case "zset": server.aofwriter.AppendZSet(snap.GetSortedSet(string(key))) case "hash": server.aofwriter.AppendHash(snap.GetHash(string(key))) case "set": server.aofwriter.AppendSet(snap.GetSet(string(key))) case "list": server.aofwriter.AppendList(snap.GetList(string(key))) case "string": server.aofwriter.AppendString(key, value) case "doc": server.aofwriter.AppendDoc(snap.GetDoc(string(key))) case "none": stdlog.Println("bad key type", string(key), string(value)) default: stdlog.Println("bad key type", string(key), string(keytype), string(value)) } }) snap.Close() snapclosed = true if server.aofwriter.IsClosed() { return errors.New("aof closed") } seq := lastseq + 1 deplymsec := 10 for { if server.aofwriter.IsClosed() { return errors.New("aof closed") } var val []byte val, err = server.synclog.Read(seq) if err != nil { stdlog.Printf("aof synclog read error %s\n", err) break } if val == nil { time.Sleep(time.Millisecond * time.Duration(deplymsec)) deplymsec += 10 if deplymsec >= 10000 { deplymsec = 10 } continue } else { deplymsec = 10 } server.aofwriter.Write(val) server.aofwriter.Flush() seq++ } return nil }
// ServerHandler.SessionClosed() func (server *GoRedisServer) SessionClosed(session *Session, err error) { server.counters.Get("connection").Incr(-1) server.sessmgr.Remove(session.RemoteAddr().String()) stdlog.Println("end connection", session.RemoteAddr(), err) }
func (server *GoRedisServer) ExceptionCaught(err error) { stdlog.Printf("exception %s\n", err) stdlog.Println(string(debug.Stack())) }