咱們今天開始第一部分「golang技能提高」。這一塊我計劃分析3個項目,一個是很流行的golang源碼閱讀入門項目cache2go,接着是很是流行的memcache的go語言版groupcache,最後是docker項目中分出來的核心組件之一containerd,經過這3個項目的源碼全解析來達到golang能力上一個臺階的目的。git
在本系列教程的學習過程當中,我但願你可以作到以下要求:若是你是一個go語言新手,在看下面的代碼分析過程當中你確定會遇到一些本身陌生的編碼方式、陌生的知識點,這個過程當中我但願你遇到一個點掌握一個點,好比看到下面的代碼用到了鎖,就去找各類資料把鎖相關的知識點學了。看到回調函數,就思考一下人家爲何這些地方都使用回調函數,有什麼好處。這樣在看完這個項目源碼分析後,你就能學到一部分知識模塊github
這是一個在github上開源的項目,原做者這樣介紹:golang
Concurrency-safe golang caching library with expiration capabilities.docker
看懂了嗎?簡單說就是有心跳機制的併發安全的go語言緩存庫。ok,下面咱們要分析的這個項目是一個緩存庫,而且有2大特性,併發安全和心跳機制!緩存
項目目錄結構如上圖所示,能夠看到功能實現相關源碼文件只有3個:安全
cache.go數據結構
cacheitem.go併發
cachetable.goapp
項目中只涉及到2個複雜的數據類型,分別是:函數
CacheItem
CacheTable
含義和字面意義一致,一個是緩存表,一個是緩存表中的條目。下面分別看一下這2個結構是怎麼定義的。
一、CacheItem
CacheItem類型是用來表示一個單獨的緩存條目,源碼以下所示,每一個字段都很清晰易懂,註釋稍長的屬性已經中文標註。
1// CacheItem is an individual cache item
2// Parameter data contains the user-set value in the cache.
3type CacheItem struct {
4 sync.RWMutex
5
6 // The item's key.
7 key interface{}
8 // The item's data.
9 data interface{}
10 //【不被訪問後存活時間】
11 // How long will the item live in the cache when not being accessed/kept alive.
12 lifeSpan time.Duration
13
14 // Creation timestamp.
15 createdOn time.Time
16 // Last access timestamp.
17 accessedOn time.Time
18 // How often the item was accessed.
19 accessCount int64
20 //【被刪除時觸發的回調方法】
21 // Callback method triggered right before removing the item from the cache
22 aboutToExpire func(key interface{})
23}
二、CacheTable
CacheTable描述了緩存中的一個表項,裏面的items屬性就是上面講到的CacheItem類型實例。這裏除了常規屬性外還有若干函數類型的屬性,源碼以下所示(最後幾行顯示樣式好像抽筋了~):
1// CacheTable is a table within the cache
2type CacheTable struct {
3 sync.RWMutex
4
5 // The table's name.
6 name string
7 // All cached items.
8 //【一個表中的全部條目都存在這個map裏,map的key是任意類型,值是CacheItem指針類型】
9 items map[interface{}]*CacheItem
10
11 // Timer responsible for triggering cleanup.
12 //【負責觸發清除操做的計時器】
13 cleanupTimer *time.Timer
14 // Current timer duration.
15 //【清除操做觸發的時間間隔】
16 cleanupInterval time.Duration
17
18 // The logger used for this table.
19 logger *log.Logger
20
21 // Callback method triggered when trying to load a non-existing key.
22 //【須要提取一個不存在的key時觸發的回調函數】
23 loadData func(key interface{}, args ...interface{}) *CacheItem
24 // Callback method triggered when adding a new item to the cache.
25 //【增長一個緩存條目時觸發的回調函數】
26 addedItem func(item *CacheItem)
27 // Callback method triggered before deleting an item from the cache.
28 //【刪除前觸發的回調函數】
29 aboutToDeleteItem func(item *CacheItem)
30}
如上所示,cache2go的核心數據結構很簡潔,應該比較容易理解。固然沒有徹底理解每一個字段這樣定義的緣由也別急,看完下面的代碼邏輯後反過來再看數據結構,確定就明白每一個字段的做用了。
5、代碼邏輯
咱們的思路是從下往上分析代碼,什麼意思呢?就是說先看和item相關的操做,再看包含item的table相關的代碼,最後看操做table的cache級別的邏輯。因此下面咱們要先看cacheitem.go的代碼,接着分析cachetable.go,而後看cache.go,最後看3個文件中的源碼互相間關聯是什麼,最後看example,也就是cache怎麼玩~
一、cacheitem.go
如上圖,這個源碼文件中只包含了一個類型CacheItem和一個函數NewCacheItem()的定義。CacheItem有哪些屬性前面已經看過了,下面先看NewCacheItem()函數:
1// NewCacheItem returns a newly created CacheItem.
2// Parameter key is the item's cache-key.
3// Parameter lifeSpan determines after which time period without an access the item
4// will get removed from the cache.
5// Parameter data is the item's value.
6func NewCacheItem(key interface{}, lifeSpan time.Duration, data interface{}) *CacheItem {
7 t := time.Now()
8 return &CacheItem{
9 key: key,
10 lifeSpan: lifeSpan,
11 createdOn: t,
12 accessedOn: t,
13 accessCount: 0,
14 aboutToExpire: nil,
15 data: data,
16 }
17}
代碼如上所示,NewCacheItem()函數接收3個參數,分別是鍵、值、存活時間(key、data、lifeSpan),返回一個CacheItem類型實例的指針。
其中createOn和accessedOn設置成了當前時間,aboutToExpire也就是被刪除時觸發的回調方法暫時設置成nil,不難想到這個函數完成後還須要調用其餘方法來設置這個屬性。
cacheitem.go中除了CacheItem類型的定義,NewCacheItem()函數的定義外,還有一個部分就是CacheItem的方法定義,一共8個
源碼看起來行數很多,內容其實很簡單,主要是元素獲取操做,這裏須要留意讀寫操做都是加了相應的讀寫鎖的,還記得開頭提到的cache2go是一個併發安全的程序嗎?併發安全就體如今這些地方。下面最複雜的是最後一個回調函數的設置,這個方法的形參是f func(interface{}),也就是說形參名爲f,形參類型是func(interface{}),這是一個函數類型,這個函數類型的參數是一個interface{},也就是空接口,由於任意類型均可以被認爲實現了空接口,因此這裏能夠接收任意類型的實參。也就是說f的類型是一個能夠接收任意類型參數的函數類型。有點繞,須要靜下心來理解一下哦~
源碼以下:
1// KeepAlive marks an item to be kept for another expireDuration period.
2//【將accessedOn更新爲當前時間】
3func (item *CacheItem) KeepAlive() {
4 item.Lock()
5 defer item.Unlock()
6 item.accessedOn = time.Now()
7 item.accessCount++
8}
9
10// LifeSpan returns this item's expiration duration.
11func (item *CacheItem) LifeSpan() time.Duration {
12 // immutable
13 return item.lifeSpan
14}
15
16// AccessedOn returns when this item was last accessed.
17func (item *CacheItem) AccessedOn() time.Time {
18 item.RLock()
19 defer item.RUnlock()
20 return item.accessedOn
21}
22
23// CreatedOn returns when this item was added to the cache.
24func (item *CacheItem) CreatedOn() time.Time {
25 // immutable
26 return item.createdOn
27}
28
29// AccessCount returns how often this item has been accessed.
30func (item *CacheItem) AccessCount() int64 {
31 item.RLock()
32 defer item.RUnlock()
33 return item.accessCount
34}
35
36// Key returns the key of this cached item.
37func (item *CacheItem) Key() interface{} {
38 // immutable
39 return item.key
40}
41
42// Data returns the value of this cached item.
43func (item *CacheItem) Data() interface{} {
44 // immutable
45 return item.data
46}
47
48// SetAboutToExpireCallback configures a callback, which will be called right
49// before the item is about to be removed from the cache.
50//【設置回調函數,當一個item被移除的時候這個函數會被調用】
51func (item *CacheItem) SetAboutToExpireCallback(f func(interface{})) {
52 item.Lock()
53 defer item.Unlock()
54 item.aboutToExpire = f
55}
到這裏就看完這個源文件了,是否是很輕鬆呢~
上面的一堆方法功能都仍是很直觀的,今天咱們先看到這裏,下一講繼續分析cachetable相關代碼。