Google 出的 Guava 是个什么鬼
轉載自? ?Google 出的 Guava 是個什么鬼
前言
Google 出的?Guava?是 Java 核心增強的庫,應用非常廣泛。
我平時用的也挺頻繁,這次就借助日常使用的 Cache 組件來看看 Google 大牛們是如何設計的。
緩存
本次主要討論緩存。
緩存在日常開發中舉足輕重,如果你的應用對某類數據有著較高的讀取頻次,并且改動較小時那就非常適合利用緩存來提高性能。
緩存之所以可以提高性能是因為它的讀取效率很高,就像是 CPU 的?L1、L2、L3?緩存一樣,級別越高相應的讀取速度也會越快。
但也不是什么好處都占,讀取速度快了但是它的內存更小資源更寶貴,所以我們應當緩存真正需要的數據。
其實也就是典型的空間換時間。
下面談談 Java 中所用到的緩存。
JVM 緩存
首先是 JVM 緩存,也可以認為是堆緩存。
其實就是創建一些全局變量,如?Map、List?之類的容器用于存放數據。
這樣的優勢是使用簡單但是也有以下問題:
-
只能顯式的寫入,清除數據。
-
不能按照一定的規則淘汰數據,如?LRU,LFU,FIFO?等。
-
清除數據時的回調通知。
-
其他一些定制功能等。
Ehcache、Guava Cache
所以出現了一些專門用作 JVM 緩存的開源工具出現了,如本文提到的 Guava Cache。
它具有上文 JVM 緩存不具有的功能,如自動清除數據、多種清除算法、清除回調等。
但也正因為有了這些功能,這樣的緩存必然會多出許多東西需要額外維護,自然也就增加了系統的消耗。
分布式緩存
剛才提到的兩種緩存其實都是堆內緩存,只能在單個節點中使用,這樣在分布式場景下就招架不住了。
于是也有了一些緩存中間件,如 Redis、Memcached,在分布式環境下可以共享內存。
具體不在本次的討論范圍。
Guava Cache 示例
之所以想到 Guava 的 Cache,也是最近在做一個需求,大體如下:
從 Kafka 實時讀取出應用系統的日志信息,該日志信息包含了應用的健康狀況。 如果在時間窗口 N 內發生了 X 次異常信息,相應的我就需要作出反饋(報警、記錄日志等)。
對此 Guava 的 Cache 就非常適合,我利用了它的 N 個時間內不寫入數據時緩存就清空的特點,在每次讀取數據時判斷異常信息是否大于 X 即可。
偽代碼如下:
首先是構建了 LoadingCache 對象,在 N 分鐘內不寫入數據時就回收緩存(當通過 Key 獲取不到緩存時,默認返回 0)。
然后在每次消費時候調用?checkAlert()?方法進行校驗,這樣就可以達到上文的需求。
我們來設想下 Guava 它是如何實現過期自動清除數據,并且是可以按照 LRU 這樣的方式清除的。
大膽假設下:
內部通過一個隊列來維護緩存的順序,每次訪問過的數據移動到隊列頭部,并且額外開啟一個線程來判斷數據是否過期,過期就刪掉。有點類似于我之前寫過的?動手實現一個 LRU cache
胡適說過:大膽假設小心論證
下面來看看 Guava 到底是怎么實現。
原理分析
看原理最好不過是跟代碼一步步走了:
示例代碼在這里:
https://github.com/crossoverJie/Java-Interview/blob/master/src/main/java/com/crossoverjie/guava/CacheLoaderTest.java
為了能看出 Guava 是怎么刪除過期數據的在獲取緩存之前休眠了 5 秒鐘,達到了超時條件。
最終會發現在?com.google.common.cache.LocalCache?類的 2187 行比較關鍵。
再跟進去之前第 2182 行會發現先要判斷 count 是否大于 0,這個 count 保存的是當前緩存的數量,并用 volatile 修飾保證了可見性。
更多關于 volatile 的相關信息可以查看?你應該知道的 volatile 關鍵字
接著往下跟到:
2761 行,根據方法名稱可以看出是判斷當前的 Entry 是否過期,該 entry 就是通過 key 查詢到的。
這里就很明顯的看出是根據根據構建時指定的過期方式來判斷當前 key 是否過期了。
如果過期就往下走,嘗試進行過期刪除(需要加鎖,后面會具體討論)。
到了這里也很清晰了:
-
獲取當前緩存的總數量
-
自減一(前面獲取了鎖,所以線程安全)
-
刪除并將更新的總數賦值到 count。
其實大體上就是這個流程,Guava 并沒有按照之前猜想的另起一個線程來維護過期數據。
應該是以下原因:
-
新起線程需要資源消耗。
-
維護過期數據還要獲取額外的鎖,增加了消耗。
而在查詢時候順帶做了這些事情,但是如果該緩存遲遲沒有訪問也會存在數據不能被回收的情況,不過這對于一個高吞吐的應用來說也不是問題。
總結
最后再來總結下 Guava 的 Cache。
其實在上文跟代碼時會發現通過一個 key 定位數據時有以下代碼:
如果有看過?ConcurrentHashMap 的原理?應該會想到這其實非常類似。
其實 Guava Cache 為了滿足并發場景的使用,核心的數據結構就是按照 ConcurrentHashMap 來的,這里也是一個 key 定位到一個具體位置的過程。
先找到 Segment,再找具體的位置,等于是做了兩次 Hash 定位。
上文有一個假設是對的,它內部會維護兩個隊列?accessQueue,writeQueue?用于記錄緩存順序,這樣才可以按照順序淘汰數據(類似于利用 LinkedHashMap 來做 LRU 緩存)。
同時從上文的構建方式來看,它也是構建者模式來創建對象的。
因為作為一個給開發者使用的工具,需要有很多的自定義屬性,利用構建則模式再合適不過了。
Guava 其實還有很多東西沒談到,比如它利用 GC 來回收內存,移除數據時的回調通知等。之后再接著討論。
總結
以上是生活随笔為你收集整理的Google 出的 Guava 是个什么鬼的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 就当是一场梦醒了很久还是很感动 是哪首歌
- 下一篇: 什么是荧光猪肉