日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

剖析Jive的缓存机制

發布時間:2025/3/14 编程问答 16 豆豆
生活随笔 收集整理的這篇文章主要介紹了 剖析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類的源碼為:

public class LinkedListNode {public LinkedListNode previous;public LinkedListNode next;public Object object;public long timestamp;public LinkedListNode(Object object, LinkedListNode next,LinkedListNode previous){this.object = object;this.next = next;this.previous = previous;}public void remove() {previous.next = next;next.previous = previous;}public String toString() {return object.toString();} }


  很明顯,這是一個雙向鏈表的節點類,previous、next分別記錄前后節點的指針,object用于記錄所需緩存的對象,timestamp用于記錄當前節點被創建時的時間戳。當該時間戳超過該節點的生存周期時,它就會被remove()方法刪除掉。該類主要完成的功能就是由LinkedListNode構成LinkedList鏈表,而由LinkedList類實現getFirst()、getLast()、addFirst()、addLast()、clear()等鏈表的基本方法。

  再來看Cacheable接口和它的一個實現類CacheableInt的源碼:

public interface Cacheable {public int getSize(); } public class CacheableInt implements Cacheable {private int intValue;public CacheableInt(int intValue) {this.intValue = intValue;}public int getInt() {return intValue;}public int getSize() {return CacheSizes.sizeOfObject() + CacheSizes.sizeOfInt();} }


  從上面的代碼可以看到,Cacheable接口只有一個方法getSize()。它要求所有繼承類實現該方法,并輸出占用緩存的大小,以便實施管理。那么為什么CacheableInt. getSize()得到的是sizeOfObject()+sizeOfInt()呢?因為任何類都繼承自Object,計算空間時當然也要把它算上了。

  還有一個CacheObject類,它是緩存的基本元素,來看一下它的代碼:

public final class CacheObject {public Cacheable object;public int size;public LinkedListNode lastAccessedListNode;public LinkedListNode ageListNode;public CacheObject(Cacheable object, int size) {this.object = object;this.size = size;} }


  lastAccessedListNode記錄著一個緩存節點的Key值,是構成lastAccessedList鏈表的基本元素,在lastAccessedList鏈表中,經常被訪問到的節點總是在最前面。ageListNode記錄著緩存節點的加入時間,是構成ageList鏈表的基本元素。而ageList鏈表是按時間先后排序,先加入的節點總是在最后面。lastAccessedListNode和ageListNode本來可以分寫成兩個類,畢竟lastAccessedListNode并不需要ageListNode的成員變量timestamp,但是為了簡化程序,Jive把它們寫成了一個類。這也是值得注意的一個地方。

  現在來看緩存機制中最關鍵的一個類Cache的部分代碼,其中主要是add()和get()方法。有關這兩個方法的介紹請參考代碼中的注釋。

public class Cache implements Cacheable {protected static long currentTime = CacheTimer. currentTime;protected HashMap cachedObjectsHash;protected LinkedList lastAccessedList;protected LinkedList ageList;//緩存元素的最大尺寸128KB,可修改protected int maxSize = 128 * 1024; //整個緩存的大小protected int size = 0;//緩存元素的最大保存時間,用Cache(long maxLifetime)初始化protected long maxLifetime = -1;//記錄cache的命中次數和未命中次數protected long cacheHits, cacheMisses = 0L; ......//向哈希表中添加一個關鍵字為Key的緩存對象objectpublic synchronized void add(Object key, Cacheable object) {//先把原來的對象remove掉remove(key);int objectSize = object.getSize();//如果對象太大,則不加入緩存if (objectSize > maxSize * .90) {return;}size += objectSize;//新建一個緩存對象,并放入哈希表中CacheObject cacheObject = new CacheObject(object, objectSize);cachedObjectsHash.put(key, cacheObject);// 把緩存元素的Key放到lastAccessed List鏈表的最前面LinkedListNode lastAccessedNode = lastAccessedList.addFirst(key);cacheObject.lastAccessedListNode = lastAccessedNode;//把緩存元素的Key放到ageList鏈表的最前面,并記下當前時間LinkedListNode ageNode = ageList.addFirst(key);ageNode.timestamp = System.currentTimeMillis();cacheObject.ageListNode = ageNode;// 在cullCache()中,先調用deleteExpiredEntries()把過期對象刪掉,如果緩存還是太滿,則調用 remove(lastAccessedList.getLast().object)把lastAccessedList中不常訪問的對象刪掉cullCache();}//在哈希表中得到一個關鍵字為Key的緩存對象objectpublic synchronized Cacheable get(Object key) {// 清理過期對象deleteExpiredEntries();CacheObject cacheObject = (CacheObject)cachedObjectsHash.get(key);if (cacheObject == null) {//沒找到則未命中次數加一cacheMisses++;return null;}//找到則命中次數加一cacheHits++;//將該緩存對象從lastAccessedList鏈表中取下并插入到鏈表頭部cacheObject.lastAccessedListNode.remove();lastAccessedList.addFirst(cacheObject.lastAccessedListNode);return cacheObject.object;} }


  到這里,第一部分的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.java public class DbForum implements Forum, Cacheable { ......public ForumThread getThread(long threadID)throws ForumThreadNotFoundException{return factory.getThread(threadID, this);}...... }DbForumFactory.java public class DbForumFactory extends ForumFactory {......protected DbForumThread getThread(long threadID, DbForum forum)throws ForumThreadNotFoundException{DbForumThread thread = cacheManager.threadCache.get(threadID);return thread;} ...... }ForumThreadCache.java public class ForumThreadCache extends DatabaseCache {......public DbForumThread get(long threadID)throwsForumThreadNotFoundException{ //緩存中尋找以threadID為編號的DbForumThread對象DbForumThread thread = (DbForumThread)cache.get(threadID);if (thread == null) {//如果在緩存中找不到該對象//新建一個以threadID為編號的DbForumThread對象thread = new DbForumThread(threadID, factory);//將新建對象加入緩存cache.add(threadID, thread);}return thread;}...... }DbForumThread.java public class DbForumThread implements ForumThread, Cacheable {......protected DbForumThread(long id, DbForumFactory factory)throws ForumThreadNotFoundException{this.id = id;this.factory = factory;//讀取數據庫,其中id對應數據庫中的jiveThreadProp表中的threadID字段loadFromDb();isReadyToSave = true;}...... }


  從上面的代碼可以看到,當調用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 filter = new ResultFilter(); filter.setStartIndex(start); filter.setNumResults(range); //調用Dbforum的threads()方法,獲得ForumThreadIterator對象實例 ForumThreadIterator threads = forum.threads(filter); ...... while (threads.hasNext()) {//對thead進行遍歷ForumThread thread = (ForumThread)threads.next();//得到thread的IDlong threadID = thread.getID();//得到線索的根帖子rootMessageForumMessage rootMessage = thread.getRootMessage();//得到帖子主題和作者等信息String subject = rootMessage.getSubject();User author = rootMessage.getUser();...... } %> DbForum.java public class DbForum implements Forum, Cacheable {......public ForumThreadIterator threads(ResultFilter resultFilter) {//生成SQL語句String query = getThreadListSQL(resultFilter, false);//得到threadID塊long [] threadBlock = getThreadBlock(query.toString(),resultFilter.getStartIndex());......//返回ForumThreadBlockIterator對象return new ForumThreadBlockIterator(threadBlock, query.toString(),startIndex, endIndex, this.id, factory);}protected long[] getThreadBlock(String query, int startIndex) {int blockID = startIndex / THREAD_BLOCK_SIZE;int blockStart = blockID * THREAD_BLOCK_SIZE; String key = query + blockID; //根據Key的值到緩存中取得ThreadID的數組CacheableLongArray longArray =(CacheableLongArray)threadListCache.get(key);//在緩存中則返回if (longArray != null) {long [] threads = longArray.getLongArray();return threads;}// 否則到數據庫中取ThreadID的塊,以數組形式返回else {LongList threadsList = new LongList(THREAD_BLOCK_SIZE);Connection con = null;Statement stmt = null;...數據庫操作 ...}long [] threads = threadsList.toArray();//將 ThreadID的塊加入緩存threadListCache.add(key, new CacheableLongArray(threads));return threads;} ...... }


  在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的缓存机制的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。