剖析Jive的缓存机制
生活随笔
收集整理的這篇文章主要介紹了
剖析Jive的缓存机制
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
Jive是一個廣受歡迎的開放源碼的論壇項目,雖然推出了很多年,但至今很多Java程序員還對它津津樂道。從框架結構上看,它采用了很多設計模式,如Factory模式、Proxy模式、Decorator模式、Iterator模式,使得程序易于擴展和移植。從設計細節上看,它采用了很多先進的設計思想和方法,如XML讀寫配置文件、數據庫的緩存和連接池、帖子的過濾和TreeWalk遍歷等,使得程序更加強健和高效。本文主要談的是Jive緩存機制的實現。 簡介 大家知道,在兩個存取速度差距很大的對象(比如數據庫和內存)之間,通常要加一個緩存來匹配二者的速度。因此,緩存機制在實際項目中還是經常遇到的。同樣Jive也使用緩存來加快貼子的顯示。如果試圖編寫一個類似的程序,不妨研究一下Jive源碼,可能對你大有幫助。 在Jive 2.1.2中,涉及Jive緩存機制的Java類大致可以分為以下四個部分(為了簡化起見,本文只討論帖子緩存機制的實現。用戶名和權限的存取雖然也用到了緩存,但其實現機制與前者類似,因此不再贅述): 第一部分,提供HashMap、LinkedListedlist等數據結構,以便實現緩存機制,其中HashMap是JDK提供的,其Key類型為Object。可以在com.jivesoftware.util包中找到這些數據結構。此部分包括Cache類、 LinkedList類、LinkedListNode類、Casheable接口、CacheObject類、CacheableBoolean類、CacheableInt類、CacheableLong類、CacheableLongArray類、CacheableString類、CacheSizes類、CacheTimer類。 第二部分,提供LongHashMap、LongLinkedListedlist等數據結構以實現緩存機制。與第一部分不同的是,它的HashMap是自己編寫的,其Key為Long型,因此被冠以LongHashMap的名稱。同樣可以在com.jivesoftware.util包中找到它們。該部分包括LongHashMap類、LongCache類、 LongCacheObject類、LongLinkedList類和LongLinkedListNode類。還有第一部分中的Casheable接口,它的各種數據類型的實現、CacheSizes類和CacheTimer類,也可歸于這部分。它們可看作是第一部分和第二部分的交集。 第三部分,調用底層數據結構以提供論壇對象的緩存。可以在com.jivesoftware.forum.database包中找到這些底層數據結構。該部分包括的類主要有DatabaseCacheManager類、DbForumFactory類、DbForum類、DbForumThread類、DbForumMessage 類、DatabaseCache類、ForumCache類、 ForumThreadCache類和ForumMessageCache類; 第四部分,向Jsp頁面提供訪問接口,同樣可以在com.jivesoftware.forum.database包中找到這些接口。該部分包括的類有ForumThreadBlockIterator類和ForumMessageBlockIterator類,第三部分的DbForum類、DbForumThread類和DbForumMessage 類也可以包括進來。實際上,這三個類是第三部分和第四部分聯系的紐帶。在com.jivesoftware.util包中還有一個LongList類,它用來將ForumThreadBlockIterator類和ForumMessageBlockIterator類轉化成Long型數組,因此也應算在這部分。 從上面介紹可看出,緩存機制也可以劃分為三層,即第一和第二部分的底層數據結構,第三部分的中間層和第四部分的上層訪問接口,下面分別討論它們。 底層數據結構 Jive緩存機制的原理其實很簡單,就是把所要緩存的對象加到HashMap哈希映射表中,用兩個LinkedListedlist雙向鏈表分別維持著緩存對象和每個緩存對象的生命周期。如果一個緩存對象被訪問到,那么就把它放到鏈表的最前面,然后不定時地把要緩存的對象加入鏈表中,把過期對象刪除,如此反復。實際上比較第一和第二部分就可以發現,它們的代碼幾乎完全相同。差別就在第二部分的哈希映射表沒有采用JDK提供的類,而是采用了作者自己編寫的一個類,將原來哈希映射表的Key類型由Object改為Long。這樣做雖然在一定程度上加快了緩存的速度,并減小了緩存的大小,但無形之中也減低了程序的穩定性和可讀性,因此不推薦仿效。值得一提的是,在Jive 1.0.2版中,所有Forum、Thread、Message的ID和它們內容的緩存都是用第一部分的Java類實現的。它在升級到后面的版本時,其內容采用了第二部分的Java類實現,但其ID仍用第一部分的Java類實現,這是Jive中值得注意的一個地方。下面先來看第一部分的Java類實現。LinkedListNode類的源碼為:
很明顯,這是一個雙向鏈表的節點類,previous、next分別記錄前后節點的指針,object用于記錄所需緩存的對象,timestamp用于記錄當前節點被創建時的時間戳。當該時間戳超過該節點的生存周期時,它就會被remove()方法刪除掉。該類主要完成的功能就是由LinkedListNode構成LinkedList鏈表,而由LinkedList類實現getFirst()、getLast()、addFirst()、addLast()、clear()等鏈表的基本方法。 再來看Cacheable接口和它的一個實現類CacheableInt的源碼:
從上面的代碼可以看到,Cacheable接口只有一個方法getSize()。它要求所有繼承類實現該方法,并輸出占用緩存的大小,以便實施管理。那么為什么CacheableInt. getSize()得到的是sizeOfObject()+sizeOfInt()呢?因為任何類都繼承自Object,計算空間時當然也要把它算上了。 還有一個CacheObject類,它是緩存的基本元素,來看一下它的代碼:
lastAccessedListNode記錄著一個緩存節點的Key值,是構成lastAccessedList鏈表的基本元素,在lastAccessedList鏈表中,經常被訪問到的節點總是在最前面。ageListNode記錄著緩存節點的加入時間,是構成ageList鏈表的基本元素。而ageList鏈表是按時間先后排序,先加入的節點總是在最后面。lastAccessedListNode和ageListNode本來可以分寫成兩個類,畢竟lastAccessedListNode并不需要ageListNode的成員變量timestamp,但是為了簡化程序,Jive把它們寫成了一個類。這也是值得注意的一個地方。 現在來看緩存機制中最關鍵的一個類Cache的部分代碼,其中主要是add()和get()方法。有關這兩個方法的介紹請參考代碼中的注釋。
到這里,第一部分的Java類實現就介紹完了。正如上文提到的那樣,第二部分的Java類實現與第一部分基本上沒有什么差別,因此就不再贅述。下面給出第二部分的類圖(見圖1),以供讀者參考。 圖一、Jive緩存機制的底層數據結構 中間層 中間層是聯系上層訪問接口和低層數據結構的紐帶。它的主要功能就是根據ID(對應于數據庫中的編號)到緩存中找相應的對象。如果緩存中有該對象就直接得到,沒有則去讀數據庫生成一個新的對象,再把該對象放入緩存中,以便下次訪問時能直接得到。圖2是相關類的類圖。 圖二、Jive緩存機制的中間層 圖2中Forum表示論壇,Thread表示論壇貼子的線索,Message表示論壇貼子,它們的關系是:Forum包括數條Thread,Thread包括數條Message。 由圖2可見,DbForum類、DbForumThread類和DbForumMessage類的實例對象都包含一個 DbForumFactory類的實例對象factory。DbForum類、DbForumThread類和DbForumMessage類被DbForumFactory生產出來,同時它們也通過DbForumFactory來訪問緩存。而在DbForumFactory中則包含一個DatabaseCacheManager類的實例對象cacheManager。它負責管理所有的緩存對象,這些緩存對象就是ForumCache類、ForumThreadCache類和ForumMessageCache類的實例。ForumCache類、 ForumThreadCache類和ForumMessageCache類繼承自同一個抽象類DatabaseCache,而在DatabaseCache類中,有一個LongCache型的成員變量cache。這樣中間層就和低層的數據結構結合起來了。 現在以thread線索對象的獲得為例,說明中間層是如何運作的。請看代碼摘要:
從上面的代碼可以看到,當調用DbForum類 的getThread(long threadID)方法獲得一個編號為threadID的線索對象時,實際上調用的是DbForumFactory類中的getThread(long threadID, DbForum forum)方法,而GetThread方法則是調用ForumThreadCache類的get方法來完成任務的。ForumThreadCache類里get(long threadID)方法則根據threadID到緩存中找相應的線索對象,如果緩存中有該對象就直接得到,沒有則新建一個DbForumThread對象,再把該對象放入緩存中。看到這里也許有人會奇怪,好像程序中根本沒有連接數據庫的語句。我們可以從DbForumThread類的代碼中找到答案。原來Jive在新建一個DbForumThread對象時,就已經用loadFromDb()方法把數據讀出來了。另一方面,如果在緩存中找到了DbForumThread對象,程序根本就不會新建DbForumThread對象,因而就好象沒有數據庫的操作,這實際上就是通過緩存機制所要達到的目的。 Message帖子對象的獲得與Thread對象的獲得類似,因此就不再重復了。從上面介紹可以看出,只要得到論壇線索的編號threadID,就可以得到對應的線索對象,不管它是從緩存中來,還是從數據庫中來。那么threadID是如何從Jsp頁面傳到中間層的呢?讓我們來看上層訪問接口的運行機制吧。 上層訪問接口 上層訪問接口的主要功能是連接JSP頁面和中間層。換句話說,就是把JSP頁面中要調用的Thread、Message對象的ID傳遞到中間層。下面給出訪問Thread相關類的類圖(訪問Message機制圖類似,故省略),見圖3。其中的forum.jsp是顯示論壇內容的頁面。在這里,我們把forum.jsp看成是一個特殊的類,它里面有一個ForumThreadIterator類的實例變量threads和DbForum類的實例變量forum,故它和ForumThreadIterator類及DbForum類的關系應是關聯關系。 圖三、Jive緩存機制的上層訪問接口 先來看forum.jsp和DbForum 類的部分代碼:
在forum.jsp中有一個ResultFilter類的實例resultFilter。它給出頁面顯示Thread的起始位置和數量,并作為參數傳入forum.threads()中,用于構造相關的SQL語句。當調用forum.threads(filter)時,程序將生成的SQL語句傳入到getThreadBlock()方法中得到一個threadID的塊,也就是一組threadID。之所以要讀threadID塊,是因為顯示論壇時并不是顯示一條線索就行了,而是一下顯示十幾條。這樣做可以避免反復讀數據庫,而且threadID不是thread對象,并不占太大空間。 應該說使用了塊以后,減輕了數據庫的訪問量,因而論壇的效率有了很大的提高。不僅如此,Jive又把塊放入了緩存中。在getThreadBlock()方法里,Jive用Cache類的實例對象threadListCache來緩存threadID塊,而關鍵字就是SQL語句加上blockID。也就是說,只要SQL語句和blockID相同,就可以在緩存中取出相同的threadID塊。當然,緩存中找不到,還是要到數據庫中讀出來加入緩存的,這樣論壇的效率又得到了進一步的提升。 ForumThreadBlockIterator類繼承自ForumThreadIterator抽象類,而ForumThreadIterator類又實現了Iterator接口,因此得到ForumThreadBlockIterator的實例對象threads后,就可以在用threads.next()方法對它進行編歷了。ForumThreadBlockIterator類的功能就是逐個讀取ThreadID,然后根據ThreadID返回Thread對象,由此上層訪問接口就和中間層銜接起來了。 小結 Jive的緩存機制值得學習的地方有很多,比如讀取線索時不是讀一條而是讀一個block;顯示線索的起始位置和數量用專門的一個類來管理,并且動態生成SQL語句;用一個專門的類來負責管理緩存;把論壇緩存對象的功能抽象出來形成一個緩存的抽象類DatabaseCache,讓它去跟低層數據結構聯系起來等。這些都體現了面向對象的設計原則,即提高軟件的可維護性和可復用性。 同時,Jive也告訴我們,要想編好程序,只懂條件語句和循環語句可不行,還要必須選擇好的數據結構,掌握好的面向對象的設計原則,熟悉設計模式思想方法,這樣才能編寫出強壯、高效的代碼。 |
轉載于:https://www.cnblogs.com/sunsonbaby/archive/2004/10/06/49419.html
總結
以上是生活随笔為你收集整理的剖析Jive的缓存机制的全部內容,希望文章能夠幫你解決所遇到的問題。