這是一個建立於 的文章,其中的資訊可能已經有所發展或是發生改變。 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位的(不是治本之道), 參考https://groups.google.com/group/golang-nuts/browse_thread/thread/ab1971bb9459025d#2. 大量的對象盡量使用池來管理,redis和memcache是不錯的選擇,因為這部分記憶體管理的事情交給它們了 vitess就是這麼乾的3. 手動管理也是個不錯的選擇,參考豆瓣的哥們的文章
gc的問題可以丟一邊去了,來看看memcache用戶端,還是先把最重要的貼出來,那就是memcache在vitess裡面扮演的什麼角色?答案是Row Cache裡面的cache_pool裡面的主要組件,見架構圖的底部memcache中有哪些亮點呢?老規矩,先上資料結構type Connection struct {conn net.Connbuffered 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的常見模式
}}