打造先進的內存KV數據庫-7 反射以及併發鎖

反射

反射作爲一種代碼組織形式,帶來了極大的不安全因素,同時也帶來了許多便利之處,通過方法、對象、類型名稱來獲得具體實例,可以避免大量if-else分支,使得代碼優雅,monkeyDB的服務端代碼最後採用反射組織。

併發鎖

多線程訪問同一資源時,需要對資源加鎖,否則可能會得到預料之外的後果。由於內存數據庫優越的讀寫性能,鎖的粒度可以儘量大,monkeyDB使用庫級鎖(相當於表級鎖)來保證線程安全。
MonkeyDB項目已開源至 https://github.com/InsZVA/MonkeyDB 採用GPL協議

代碼

//server.go
package main
// #cgo LDFLAGS: -L ./lib -lmonkeyS
// #include "./lib/core.h"
// #include <stdlib.h>
import "C"
import (
    "unsafe"
    "fmt"
    "net"
    "strings"
    "./tcp"
    "./convert"
    "reflect"
    "./minheap"
    "strconv"
    "flag"
    "sync"
)

////////////////////////////////////// 庫級鎖   ////////////////////////////////////////////////////////////////

var mutex map[C.Database]*sync.RWMutex

////////////////////////////////////// MinHeap ////////////////////////////////////////////////////////////////

///////////////////////////////////////////////////////////////////////////////////////////////////////////////
////////////////////////////////////// command類型 用於解析處理各種數據庫命令 //////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////////////////////////////////////
var (
    acceptedCmd = []string{"set","get","delete","remove","createdb","switchdb","dropdb",/*"push","pop","destroy",*/"listdb"}
    port = flag.String("p","1517","偵聽端口號")
)

type command []byte

func (cmd command) Set(db **C.Database) []byte {
    //鎖併發
    _,ok := mutex[**db];
    if !ok {
        mutex[**db] = new(sync.RWMutex)
    }
    mutex[**db].Lock()
    defer mutex[**db].Unlock()

    response := []byte{}
    key,next := convert.ParseUntil(cmd,' ',4)
    value,_ := convert.ParseUntil(cmd,0,next+1)
    //fmt.Println("set ",key,value)
    r := C.Set(&(*db).tIndex,(*C.char)(convert.Bytes2C(key)),(convert.Bytes2C(value)))
    for i := 0;;i++ {
        response = append(response,byte(r.msg[i]))
        if response[i] == 0 { break; }
    }
    return response
}

func (cmd command) Get(db **C.Database) []byte {
    //鎖併發
    _,ok := mutex[**db];
    if !ok {
        mutex[**db] = new(sync.RWMutex)
    }
    mutex[**db].RLock()
    defer mutex[**db].RUnlock()

    response := []byte{}
    key,_ := convert.ParseUntil(cmd,0,4)
    r := C.Get(&(*db).tIndex,(*C.char)(convert.Bytes2C(key)))
    if int(r.code) == 0 {
        for i := 0;;i++ {
            response = append(response,byte(*(*C.char)(unsafe.Pointer((uintptr(r.pData)+uintptr(i))))))
            if response[i] == 0 { break; }
        }
    }else {
    }
    return response
}

func (cmd command) Delete(db **C.Database) []byte {
    return cmd.Remove(db)
}

func (cmd command) Remove(db **C.Database) []byte {
    //鎖併發
    _,ok := mutex[**db];
    if !ok {
        mutex[**db] = new(sync.RWMutex)
    }
    mutex[**db].Lock()
    defer mutex[**db].Unlock()

    response := []byte{}
    key,_ := convert.ParseUntil(cmd,0,7)
    r := C.Delete(&(*db).tIndex,(*C.char)(convert.Bytes2C(key)))
    for i := 0;;i++ {
        response = append(response,byte(r.msg[i]))
        if response[i] == 0 { break; }
    }
    return response
}

func (cmd command) Createdb(db **C.Database) []byte {
    response := []byte{}
    key,_ := convert.ParseUntil(cmd,0,9)
    d := C.CreateDB((*C.char)(convert.Bytes2C(key)))
    if d != nil {
        *db = d
        response = []byte("Already exist,switched\n")
    }else {
        response = []byte("Created\n")
    }
    return response
}

func (cmd command) Switchdb(db **C.Database) []byte {
    response := []byte{}
    key,_ := convert.ParseUntil(cmd,0,9)
    d := C.SwitchDB((*C.char)(convert.Bytes2C(key)))
    if d != nil {
        *db = d
        response = []byte("ok\n")
    }else {
        response = []byte("fail\n")
    }
    return response
}

func (cmd command) Dropdb(db **C.Database) []byte {
    //鎖併發
    _,ok := mutex[**db];
    if !ok {
        mutex[**db] = new(sync.RWMutex)
    }
    mutex[**db].Lock()
    defer mutex[**db].Unlock()

    response := []byte{}
    key,_ := convert.ParseUntil(cmd,0,7)
    *db = C.DropDB((*C.char)(convert.Bytes2C(key)))
    return response
}

func (cmd command) Listdb(db **C.Database) []byte {
    response := []byte{}
    r := C.ListDB()
    for i := 0;i < 1024;i++ {
        b := byte(*(*C.char)(unsafe.Pointer(uintptr(unsafe.Pointer(r))+uintptr(i))))
        response = append(response,b)
        if(b == 0){ break; }
    }
    C.free(unsafe.Pointer(r))
    return response
}

func (cmd command) Push(db **C.Database) []byte {
    var response []byte
    key,next := convert.ParseUntil(cmd,' ',5)
    key2,next := convert.ParseUntil(cmd,' ',next+1)
    value,_ := convert.ParseUntil(cmd,0,next+1)
    var i int
    btnode := C.BTree_search((*db).tIndex,(C.KeyType)(C.GetHash((*C.char)(convert.Bytes2C(key)))),(*C.int)(unsafe.Pointer(&i)))
    if uintptr(unsafe.Pointer(btnode)) != uintptr(0) {  //B樹上有此堆
        heap := (*minheap.MinHeap)(btnode.pRecord[i])   //BUG GO GC 會回收掉上次的內存
        k,_ := strconv.Atoi(string(key2))
        v,_ := strconv.Atoi(string(value))
        heap.Push(minheap.Pair{Key:uint32(k),Value:uint32(v)})
        response = []byte("Pushed")
    }else {                 //建一個新的堆
        heap := minheap.New()
        C.BTree_insert(&(*db).tIndex, (C.KeyType)(C.GetHash((*C.char)(convert.Bytes2C(key)))), unsafe.Pointer(&heap))
        k,_ := strconv.Atoi(string(key2))
        v,_ := strconv.Atoi(string(value))
        heap.Push(minheap.Pair{Key:uint32(k),Value:uint32(v)})
        response = []byte("Created and pushed")
    }
    return response
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////
func initDB() { //初始化數據庫

    str0 := "monkey"
    C.CreateDB((*C.char)(convert.String2C(str0)))   //創建基礎數據庫
    str0 = "config"                     //配置數據庫
    C.CreateDB((*C.char)(convert.String2C(str0)))
    key := "passwd"     //初始密碼
    data := "monkey"
    db := C.SwitchDB((*C.char)(convert.String2C(str0)))
    C.Set(&(db.tIndex),(*C.char)(convert.String2C(key)),(convert.String2C(data)))

}

func listen() {

    servicePort := ":"+*port
    tcpAddr,err := net.ResolveTCPAddr("tcp4",servicePort)
    if err != nil {
        panic(err)
    }
    l,err := net.ListenTCP("tcp",tcpAddr)   //偵聽TCP
    if err != nil {
        panic(err)
    }
    fmt.Println("Server Started on "+*port+"!")
    for{
        conn,err := l.AcceptTCP()
        conn.SetKeepAlive(true)
        conn.SetNoDelay(true)
        if err != nil {
            panic(err)
        }
        s := tcp.TCPSession{Conn:conn}
        s.Init()
        go Handler(&s)
    }
}

func main() {
    flag.Parse()
    //初始化鎖
    mutex = make(map[C.Database]*sync.RWMutex)
    initDB()
    listen()
}

func auth(s *tcp.TCPSession) bool {
    buff := s.ReadMessage() 
    params := strings.Split(string(buff)," ")
    str0 := "config"
    db := C.SwitchDB((*C.char)(convert.String2C(str0)))
    if params[0] != "auth" {
        s.SendMessage([]byte("Please auth first!"))
        return false
    }
    r := C.Get(&(db.tIndex),(*C.char)(convert.String2C("passwd")))
    if int(r.code) == 0 {
        passwd := []byte{}
        for i := 0;;i++ {
            passwd = append(passwd,byte(*(*C.char)(unsafe.Pointer((uintptr(r.pData)+uintptr(i))))))
            if passwd[i] == 0 {
                break
            }
        }
        if convert.Equal(passwd,[]byte(params[1])) {
            s.SendMessage([]byte("Auth success"))
            return true
        } else {
            s.SendMessage([]byte("Auth fail"))
            return false
        }
    }else {
        s.SendMessage([]byte("Auth success"))
        return true
    }
}

func Handler(s *tcp.TCPSession) {
    for !auth(s){
    }
    str := "monkey"                         
    db := C.SwitchDB((*C.char)(convert.String2C(str)))//環境變量-當前數據庫
    for {
        if s.Closed {
            return
        }               
        buff := s.ReadMessage()
        // if err != nil {
        //  conn.Close()
        //  break
        // }
        if len(buff) == 0 {
            return
        }
        //commands := bytes.Split(buff,[]byte{0})
        //for _,cmd := range commands {
            TranslateMessage2(s,&db,buff)
        //}                     //解析消息
    }

}

func TranslateMessage2(s *tcp.TCPSession,db **C.Database,message []byte) {
    com := command(message)
    //fmt.Println("處理:",string(message))
    response := []byte{}
    for _,cmd := range acceptedCmd {
        if convert.StartBy(message,cmd) {
            result := reflect.ValueOf(com).MethodByName(convert.UpperHead(cmd)).Call([]reflect.Value{reflect.ValueOf(db)})
            response = result[0].Interface().([]byte)
            break
        }
    }
    s.SendMessage(response)
}


////////////////////////////////////////Dumplated//////////////////////////////////////////////////////////////////////////
func TranslateMessage(s *tcp.TCPSession,db **C.Database,message []byte) {
    command := string(message)
    params := strings.Split(command," ")
    //fmt.Println(params)
    response := []byte{}
    if params[0] == "set" {
        r := C.Set(&(*db).tIndex,(*C.char)(convert.String2C(params[1])),(convert.String2C(params[2])))
        for i := 0;;i++ {
            response = append(response,byte(r.msg[i]))
            if response[i] == 0 { break; }
        }
    }else if params[0] == "get" {
        r := C.Get(&(*db).tIndex,(*C.char)(convert.String2C(params[1])))
        // for i := 0;;i++ {
        //  response = append(response,byte(r.msg[i]))
        //  if response[i] == 0 { break; }
        // }
        if int(r.code) == 0 {
            for i := 0;;i++ {
                response = append(response,byte(*(*C.char)(unsafe.Pointer((uintptr(r.pData)+uintptr(i))))))
                if response[i] == 0 { break; }
            }
        }else {
            // for i := 0;;i++ {
            // response = append(response,byte(r.msg[i]))
            // if response[i] == 0 { break; }
            // }
        }

    }else if params[0] == "delete" || params[0] == "remove" {
        r := C.Delete(&(*db).tIndex,(*C.char)(convert.String2C(params[1])))
        for i := 0;;i++ {
            response = append(response,byte(r.msg[i]))
            if response[i] == 0 { break; }
        }

    }else if params[0] == "createdb" {
        d := C.CreateDB((*C.char)(convert.String2C(params[1])))
        if d != nil {
            *db = d
            response = []byte("Already exist,switched\n")
        }else {
            response = []byte("Created\n")
        }
    }else if params[0] == "switchdb" {
        d := C.SwitchDB((*C.char)(convert.String2C(params[1])))
        if d != nil {
            *db = d
            response = []byte("ok\n")
        }else {
            response = []byte("fail\n")
        }
    }else if params[0] == "dropdb" {
        *db = C.DropDB((*C.char)(convert.String2C(params[1])))
    }else if strings.EqualFold("listdb",params[0]) {
        r := C.ListDB()
        for i := 0;i < 1024;i++ {
            b := byte(*(*C.char)(unsafe.Pointer(uintptr(unsafe.Pointer(r))+uintptr(i))))
            response = append(response,b)
            if(b == 0){ break; }
        }
        C.free(unsafe.Pointer(r))
    }else {
        //fmt.Println("unkown command:",params[0])
    }
    s.SendMessage(response)
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章