func (c *Conn) processRequest() error { req, err := c.readRequest() if err != nil { log.Error("processRequest|readRequest|%s", err.Error()) return err } cmd := strings.ToLower(string(req[0])) if def, ok := cmdMap[cmd]; ok { args := req[1:] if len(args) < def.minArgs || len(args) > def.maxArgs { c.wb.WriteString(fmt.Sprintf("-ERR wrong number of arguments for '%s' command\r\n", cmd)) } else { err = def.fun(c, args) if err != nil { log.Error("processRequest|func|%s", err.Error()) return err } } } else { c.wb.WriteString(fmt.Sprintf("-ERR unknown command '%s'\r\n", cmd)) } if err = c.wb.Flush(); err != nil { log.Error("processRequest|Flush|%s", err.Error()) return err } else { return nil } }
func makeConn(addr, pwd string) (conn *redisConn, err error) { log.Info("makeConn|%v|%v", addr, pwd) conn = nil const dataTimeout = 5 * time.Second const connTimeout = 2 * time.Second var c redis.Conn if c, err = redis.DialTimeout("tcp", addr, connTimeout, dataTimeout, dataTimeout); err != nil { log.Error("makeConn|DialTimeout|%v", err) return } if pwd != "" { if _, err = c.Do("AUTH", pwd); err != nil { log.Error("makeConn|auth|%v", err) c.Close() return } } if _, err = c.Do("get", "__test"); err != nil { log.Error("makeConn|get|%v", err) c.Close() return } log.Info("makeConn|ok|%v", addr) var now = time.Now().Unix() conn = &redisConn{c, addr, seed, now + pingInterval, now} seed++ return }
func (c *Conn) readRequest() ([][]byte, error) { count, err := c.readCount('*') if err != nil { log.Error("readRequest|readCount|%s", err.Error()) return nil, err } if count <= 0 { log.Error("readRequest|invalid_count|%v", count) return nil, ErrRequest } var r = make([][]byte, count) var i int64 for i = 0; i < count; i++ { length, err := c.readCount('$') if err != nil { log.Error("readRequest|readCount|%s", err.Error()) return nil, err } buff := make([]byte, length+2) _, err = io.ReadFull(c.rb, buff) if err != nil { log.Error("readRequest|ReadFull|%s", err.Error()) return nil, err } if buff[length+1] != '\n' || buff[length] != '\r' { log.Error("readRequest|invalid_crlf|%v", buff) return nil, ErrRequest } r[i] = buff[0:length] } return r, nil }
func (db *Db) Close() { ret := C.db_close(db.db) if ret == 0 { return } else if ret == C.EINVAL { log.Error("Close|db_close|EINVAL") return } go func() { for { ret := C.db_close(db.db) if ret == 0 { break } else if ret == C.EINVAL { log.Error("Close|db_close|EINVAL") break } else if ret == C.DB_LOCK_DEADLOCK { log.Error("Close|db_close|DB_LOCK_DEADLOCK") } else if ret == C.DB_LOCK_NOTGRANTED { log.Error("Close|db_close|DB_LOCK_NOTGRANTED") } else { log.Error("Close|db_close|unknown|%v", ret) break } time.Sleep(time.Millisecond) } }() }
func Init(configFile string) { tomlstr, err := ioutil.ReadFile(configFile) if err != nil { log.Error("read file error: %s", err) } if _, err := toml.Decode(string(tomlstr), &SConfig); err != nil { log.Error("toml.Decode error: %s", err) } log.Debug("%#v", SConfig) }
func (c *Conn) readLine() ([]byte, error) { line, err := c.rb.ReadSlice('\n') if err != nil { log.Error("readLine|ReadSlice|%s", err.Error()) return nil, err } if len(line) <= 2 { log.Error("readLine|empty") return nil, ErrRequest } if line[len(line)-2] != '\r' { log.Error("readLine|invalid") return nil, ErrRequest } return line[:len(line)-2], nil }
func NumToUint64(arg interface{}) (uint64, error) { switch v := arg.(type) { case uint64: return v, nil case int: return uint64(v), nil case int64: return uint64(v), nil case uint: return uint64(v), nil case int32: return uint64(v), nil case uint32: return uint64(v), nil case int16: return uint64(v), nil case uint16: return uint64(v), nil case int8: return uint64(v), nil case uint8: return uint64(v), nil case float64: return uint64(v), nil case float32: return uint64(v), nil default: log.Error("ToInt64|unknown_type|%T,%v\n", arg, arg) return 0, errors.DataError } }
//转换数字字符串 func StrToInt64(instr string) (int64, error) { tmp, err := strconv.ParseInt(instr, 10, 64) if err != nil { log.Error("StrToInt64|Invalid_String|%v|%v", err, instr) return 0, errors.DataError } return tmp, nil }
// 主处理:根据FORM表单信息关键字method判断业务功能 func Start(httpaddr string, httpsaddr string) { http.HandleFunc("/mysvrs", mysvrshandler) http.HandleFunc("/ver", verhandler) http.HandleFunc("/now", nowhandler) err := http.ListenAndServe(httpaddr, nil) if err != nil { log.Error("Start err: %s", err) } }
func Init() { visitfile, err := os.Open("visitcount.conf") if err != nil { log.Error("Init error: %s", err) } var buf []byte = make([]byte, 10) length, _ := visitfile.Read(buf) visitfile.Close() log.Debug("visitcount: %v, %s, %d", buf, string(buf[:length-1]), length) go refreshVisitCount() }
func readNumber(buff []byte) (int64, error) { if len(buff) == 0 { log.Error("readNumber|empty") return 0, ErrRequest } var sign int64 = 1 var r int64 = 0 for i, c := range buff { if i == 0 && c == '-' { sign = -1 } else if c < '0' || c > '9' { log.Error("readNumber|invalid|%v", int(c)) return 0, ErrRequest } else { r *= 10 r += int64(c - '0') } } return r * sign, nil }
func (c *Conn) readCount(tag byte) (int64, error) { line, err := c.readLine() if err != nil { log.Error("readCount|readLine|%s", err.Error()) return 0, err } if len(line) < 2 { log.Error("readCount|invalid|%v", line) return 0, ErrRequest } if line[0] != tag { log.Error("readCount|tag|%c", line[0]) return 0, ErrRequest } count, err := readNumber(line[1:]) if err != nil { log.Error("readCount|readNumber|%s", err.Error()) return 0, ErrRequest } return count, nil }
func PrintPanic(err interface{}) { if err != nil { log.Error("* panic:|%v", err) } for skip := 2; ; skip++ { _, file, line, ok := runtime.Caller(skip) if !ok { break } fmt.Printf("* %v : %v\n", file, line) } }
func (pool *Pool) checkPool() { var pos = 0 for pool.total < pool.max { log.Debug("checkPool|%v|%v", pool.max, pool.total) // 需要新的连接 var addrs = pool.addrs if pos >= len(addrs) { // 兜了一圈了,看看其他消息吧 // 可能会有newAddr这样的消息需要切换服务器组 log.Error("checkPool|retry_after") return } if conn, err := makeConn(addrs[pos], pool.pwd); err != nil { log.Error("checkPool|makeConn|%v", err) pos++ } else { pool.connCh <- conn pool.total++ } } }
func (w *Worker) getdb(table string, dbtype int) (*bdb.Db, error) { db := w.dbmap[table] if db == nil { var err error db, err = w.dbenv.GetDb(table, dbtype) if err != nil { log.Error("worker|GetDb|%s", err.Error()) return nil, err } w.dbmap[table] = db } return db, nil }
func (c *Conn) Start() { defer func() { c.Close() for _, v := range c.dbmap { v.Close() } if err := recover(); err != nil { PrintPanic(err) } }() for { err := c.processRequest() if err != nil { log.Error("Start|processRequest|%s", err.Error()) break } } }
func (pool *Pool) processSlaveWrite(conn *redisConn, err string) { log.Info("receive|slavewrite") // 主从切换了 if strings.HasSuffix(err, ",unknown") { conn.pingTime = time.Now().Add(2 * time.Second).Unix() pool.connCh <- conn } else { params := strings.SplitN(err, ",", 2) if len(params) != 2 { log.Error("process|slavewrite|format_error|%s", err) } else { var newip = params[1] log.Info("receive|new_master|%v", newip) pool.newMasterCh <- newip } pool.badCh <- conn } }
func (pool *Pool) checkEvent() { var timer *time.Timer for { if timer == nil { // 第一次等5秒 timer = time.NewTimer(5 * time.Second) } else { // 如果触发事件后就需要立即处理 // 但是为了防止cleanConn触发的大量badCh事件 // 所以先等待50毫秒 timer.Stop() timer = time.NewTimer(50 * time.Millisecond) } select { case newMaster := <-pool.newMasterCh: log.Info("checkEvent|newMasterCh|%v", newMaster) var found = false for i := 0; i < len(pool.addrs); i++ { if pool.addrs[i] == newMaster { log.Info("checkNewMaster|found") pool.addrs[0], pool.addrs[i] = pool.addrs[i], pool.addrs[0] pool.cleanConn() found = true break } } if !found { log.Error("checkEvent|newMasterCh|not_found|%v", newMaster) } case newAddr := <-pool.addrCh: log.Info("checkEvent|addrCh|%v", newAddr) pool.cleanConn() pool.poolAddr = newAddr case bad := <-pool.badCh: log.Info("checkEvent|badCh|%v|%v", bad.id, bad.addr) pool.total-- bad.conn.Close() case <-timer.C: return } } }
func processRequest(conn *redisConn, reqs []*redisReq) (err error) { var slaveError error log.Debug("processRequest|%v|%v", conn.id, len(reqs)) for _, req := range reqs { conn.conn.Send(req.cmd, req.args...) } err = conn.conn.Flush() if err != nil { // 发送请求失败 for _, req := range reqs { req.ch <- redisResp{nil, err} } return } for _, req := range reqs { var ok bool if err != nil { // 判断是否处于错误状态 // 处于错误状态就不用再receive了 req.ch <- redisResp{nil, err} } else { var v interface{} v, err = conn.conn.Receive() req.ch <- redisResp{v, err} if err != nil { log.Error("processRequest|Receive|%v", err) if err, ok = err.(redis.Error); ok { // redis.Error表示的是具体某个请求的数据错误 // 该类型错误不影响后续请求的处理 if strings.HasPrefix(err.Error(), "ERR slavewrite,") { slaveError = err } err = nil } } } } if slaveError != nil { err = slaveError } return }
func (pool *Pool) testConn() { // testConn每次只检查一个连接 var conn *redisConn select { case conn = <-pool.connCh: default: // 没有空闲的连接 // 表示现在比较忙 // 暂时就不检查了 return } var masterAddr = pool.addrs[0] if conn.addr != masterAddr { if newconn, err := makeConn(masterAddr, pool.pwd); err != nil { log.Error("bkWorker|makeConn|%v", err) } else { // 主服务器已经能够连上了 conn.conn.Close() conn = newconn } } if time.Now().Unix() > conn.pingTime { // 超过pingInterval,则ping一下连接 if _, err := conn.conn.Do("set", "__ping", "1"); err != nil { if strings.HasPrefix(err.Error(), "ERR slavewrite,") { pool.processSlaveWrite(conn, err.Error()) } else { log.Info("process|ping|%v", err) pool.badCh <- conn } } else { log.Debug("bgWorker|ping") pool.connCh <- conn } } else { pool.connCh <- conn } return }
func AnyToInt64(v interface{}) (int64, error) { if v == nil { log.Error("AnyToInt64|nil") return 0, errors.DataError } switch v := v.(type) { case string: if v == "" { log.Error("AnyToInt64|empty_string", v) return 0, errors.DataError } else { outdata, err := strconv.ParseInt(v, 10, 64) if err != nil { log.Error("AnyToInt64|%v|%v", v, err) return 0, errors.DataError } return outdata, nil } case []string: if len(v) == 0 || v[0] == "" { log.Error("AnyToInt64|empty_array", v) return 0, errors.DataError } else { outdata, err := strconv.ParseInt(v[0], 10, 64) if err != nil { log.Error("AnyToInt64|%v|%v", v, err) return 0, errors.DataError } return outdata, nil } case []interface{}: if len(v) == 0 { log.Error("AnyToInt64|empty_array", v) return 0, errors.DataError } else { return AnyToInt64(v[0]) } default: return NumToInt64(v) } }
func main() { runtime.GOMAXPROCS(runtime.NumCPU() * 4) signalChan := make(chan os.Signal, 1) signal.Notify(signalChan, syscall.SIGINT, syscall.SIGTERM) // master //go run main.go -h db1 -M -l 127.0.0.1:2345 -r 127.0.0.1:2346 // slave //go run main.go -h db2 -C -l 127.0.0.1:2346 -r 127.0.0.1:2345 configFile := "bdbd.conf" if len(os.Args) > 1 { configFile = os.Args[1] } configstr, err := ioutil.ReadFile(configFile) if err != nil { log.Println("ERROR: read config file failed:", err) os.Exit(1) } var config struct { Bdb bdb.BdbConfig `toml:"bdb"` Logger []mylog.LoggerDefine `toml:"logger"` Server struct { Listen string } `toml:"server"` } config.Bdb.Flush = 1 _, err = toml.Decode(string(configstr), &config) if err != nil { log.Println("ERROR: decode config failed:", err) os.Exit(1) } mylog.Init(config.Logger) mylog.Info("bdb|starting") dbenv := bdb.Start(config.Bdb) mylog.Info("bdb|started") /* db, err := dbenv.GetDb("test") if err != nil { mylog.Fatal("dberr:%s", err.Error()) } for i := 0; i < 1000000; i++ { if i%1000 == 0 { mylog.Info("%d", i) } k := fmt.Sprintf("k_%v", i) db.Set([]byte(k), []byte(k)) } db.Close() dbenv.Exit() mylog.Fatal("end") */ go func() { addr, err := net.ResolveTCPAddr("tcp", config.Server.Listen) if err != nil { mylog.Fatal("ResolveTCPAddr|%s", err.Error()) } listener, err := net.ListenTCP("tcp", addr) if err != nil { mylog.Fatal("Server|ListenTCP|%s", err.Error()) } for { client, err := listener.AcceptTCP() if err != nil { mylog.Error("Server|%s", err.Error()) } client.SetKeepAlive(true) conn := server.NewConn(client, dbenv) go conn.Start() } }() server.Start(dbenv) mylog.Info("start") <-signalChan server.Exit() dbenv.Exit() mylog.Info("bye") }
//export Error func Error(msg *C.char) { log.Error(C.GoString(msg)) }
func (w *Worker) bdbSetEx(req *bdbSetReq) { var err error if w.expiredb == nil { w.expiredb, err = w.dbenv.GetDb("__expire", bdb.DBTYPE_BTREE) if err != nil { log.Error("worker|GetDb|%s", err.Error()) req.resp <- bdbSetResp{err} return } } if w.expireindex == nil { w.expireindex, err = w.dbenv.GetDb("__expire.index", bdb.DBTYPE_BTREE) if err != nil { log.Error("worker|GetDb|%s", err.Error()) req.resp <- bdbSetResp{err} return } } txn, err := w.dbenv.Begin(bdb.DB_READ_UNCOMMITTED) if err != nil { req.resp <- bdbSetResp{err} return } defer func() { if txn != nil { txn.Abort() } }() w.seq++ err = bdb.SetExpire(w.expiredb, w.expireindex, txn, req.key, req.sec, w.seq, w.id) if err != nil { if err == bdb.ErrRepDead { w.expiredb.Close() w.expiredb = nil w.expireindex.Close() w.expireindex = nil } log.Error("worker|SetExpire|%s", err.Error()) req.resp <- bdbSetResp{err} return } table, name := bdb.SplitKey(req.key) db, err := w.getdb(table, bdb.DBTYPE_BTREE) if err != nil { w.checkerr(err, db) req.resp <- bdbSetResp{err} return } var flags uint32 = 0 if req.nooverwrite { flags = flags | bdb.DB_NOOVERWRITE } err = db.Set(txn, name, req.value, flags) if err != nil { w.checkerr(err, db) req.resp <- bdbSetResp{err} return } txn.Commit() txn = nil req.resp <- bdbSetResp{nil} }