vitess源碼閱讀筆記cache系列之memcache客戶端(兼談讓人又愛又恨的gc和golang的錯誤處理機制)


      memcache的客戶端實現文件是memcache.go,實現了memcached的協議客戶端,對於學習golang的客戶端網絡編程,

memcache.go還是非常值得一看的。奇怪的是vitess的安裝文檔沒有提到要求安裝memcached,至於為什么使用memcache,

而不是進程內的cache以節省開銷呢?vitess的文檔是這樣描述的:

Go’s existing mark-and-sweep garbage collector is sub-optimal for systems that use large amounts of 
static memory (like caches). In the case of vtocc, this would be the row cache. To alleviate this, 
we intend to use memcache for the time being. If the gc ends up addressing this, it should be 
fairly trivial to switch to an in-memory row cache
就不翻譯了。簡單來講,vitess的作者也認為進程內的cache是更好的方案,由於當前golang的gc實現還不夠理想,

所以選擇了memcache。另外這里有篇文章分享了從ruby到golang到c的歷程也說明go的gc還不夠成熟。

豆瓣的一哥們也分享了golang手動管理內存的心得,見這里

希望go team接下來的工作重點之一是修正gc的缺陷(似乎短期內不太現實),對現有gc的問題的解決辦法幾個:
1. 盡量選擇64位操作系統,不要使用32位的(不是治本之道),
2. 大量的對象盡量使用池來管理,redis和memcache是不錯的選擇,因為這部分內存管理的事情交給它們了
    vitess就是這么干的
3. 手動管理也是個不錯的選擇,參考豆瓣的哥們的文章

 

gc的問題可以丟一邊去了,來看看memcache客戶端,還是先把最重要的貼出來,那就是
memcache在vitess里面扮演的什么角色?
答案是Row Cache里面的cache_pool里面的主要組件,見架構圖的底部
memcache中有哪些亮點呢?老規矩,先上數據結構
type Connection struct {
conn     net.Conn
buffered bufio.ReadWriter
}
既然net.Conn已經實現了Read和Write接口,那么為什么要使用bufio.ReadWriter呢,猜想有2點原因
1. bufio本身的性能可能更高,因為bufferd(未實際測試)
2. 更方便,bufio.ReadWriter還實現了ReadString,ReadLine等一系列接口,可以更便捷的實現memcache的協議
    這點在接下來的源碼中可以看到,不再詳述
重點看幾個函數就行,其余的大家看代碼即可
// 學習構造Reader和Writer即可,這中方式在golang中非常普遍
func newConnection(nc net.Conn) *Connection {
     return &Connection{
        conn: nc,
        buffered: bufio.ReadWriter{
            bufio.NewReader(nc),         // 只要實現了Read接口就可以用來構造Reader
            bufio.NewWriter(nc),         // 只要實現了Write接口就可以用來構造Writer
        },
    }
}

func (self *Connection) Close() {
    self.conn.Close()
    self.conn = nil         // 用於判斷一個connection是否關閉了,見IsClosed函數
}

func (self *Connection) IsClosed()  bool {
     return self.conn == nil

 

接下來欣賞golang漂亮的錯誤處理機制

以Get函數為例: 

func (self *Connection) Get(key  string) (value [] byte, flags uint16, err error) {  // 有名字的函數返回值,以后寫個return就自動返回這些啦
    defer handleError(&err)
    value, flags, _ = self. get( " get ", key)
     return

}

沒有看到長長的try和cache了,代碼又少了5行左右。有人說了,不就是幾個大括號嘛,親,再給你幾個函數看看 


func (self *Connection) Gets(key  string ) (value [] byte , flags uint16, cas uint64, err error) { 
    defer handleError(&err)
    value, flags, cas = self. get( " gets ", key)
     return
}

func (self *Connection) Set(key  string, flags uint16, timeout uint64, value [] byte) (stored  bool, err error) {
    defer handleError(&err)
     return self.store( " set ", key, flags, timeout, value,  0), nil
}

func (self *Connection) Add(key  string, flags uint16, timeout uint64, value [] byte) (stored  bool, err error) {
    defer handleError(&err)
     return self.store( " add ", key, flags, timeout, value,  0), nil
}

func (self *Connection) Replace(key  string, flags uint16, timeout uint64, value [] byte) (stored  bool, err error) {
    defer handleError(&err)
     return self.store( " replace ", key, flags, timeout, value,  0), nil
}

func (self *Connection) Append(key  string, flags uint16, timeout uint64, value [] byte) (stored  bool, err error) {
    defer handleError(&err)
     return self.store( " append ", key, flags, timeout, value,  0), nil
}

func (self *Connection) Prepend(key  string, flags uint16, timeout uint64, value [] byte) (stored  bool, err error) {
    defer handleError(&err)
     return self.store( " prepend ", key, flags, timeout, value,  0), nil
}

func (self *Connection) Cas(key  string, flags uint16, timeout uint64, value [] byte, cas uint64) (stored  bool, err error) {
    defer handleError(&err)
     return self.store( " cas ", key, flags, timeout, value, cas), nil
}

func (self *Connection) Delete(key  string) (deleted  bool, err error) {
    defer handleError(&err)
     //  delete <key> [<time>] [noreply]\r\n
    self.writestrings( " delete  ", key,  " \r\n ")
    reply := self.readline()
     if strings.Contains(reply,  " ERROR ") {
        panic(NewMemcacheError( " Server error "))
    }
     return strings.HasPrefix(reply,  " DELETED "), nil
}

乖乖,這下可少了不少代碼行,少了50行左右,不能不說這個defer漂亮。 

說了半天還沒給出handleError的實現呢,趕緊上菜
type MemcacheError  struct {
    Message  string
}

// 可變參數,這個在golang里面也是個常見的模式
func NewMemcacheError(format  string, args ... interface{}) MemcacheError {
     return MemcacheError{fmt.Sprintf(format, args...)}
}

func (self MemcacheError) Error()  string {
     return self.Message
}

func handleError(err *error) {
     if x := recover(); x != nil {
        *err = x.(MemcacheError)         // golang是強類型的,interface需要強制要轉化一下,這也是golang的常見模式
    }
}  

 



免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM