小男孩‘自慰网亚洲一区二区,亚洲一级在线播放毛片,亚洲中文字幕av每天更新,黄aⅴ永久免费无码,91成人午夜在线精品,色网站免费在线观看,亚洲欧洲wwwww在线观看

分享

Jive 的緩存機(jī)制

 bluecrystal 2007-07-21

緩存( Cache )機(jī)制是提高系統(tǒng)運(yùn)行性能必不可少的技術(shù)。緩存機(jī)制從原理上講比較簡單,就是在原始數(shù)據(jù)第一次讀取后保存在內(nèi)存中,下次讀取時(shí),就直接從內(nèi)存中讀取。原始數(shù)據(jù)有可能保存在持久化介質(zhì)或網(wǎng)絡(luò)上。緩存機(jī)制也是代理模式的一種實(shí)現(xiàn)。

4.1  緩存原理和實(shí)現(xiàn)

Jive Cache 總體來說實(shí)現(xiàn)得不是非常精簡和有效。它是針對每個(gè)具體數(shù)據(jù)對象逐個(gè)實(shí)現(xiàn)緩沖,這種“窮盡”的辦法不是實(shí)踐所推薦的用法。通過使用動(dòng)態(tài)代理模式,可以根據(jù)具體方法的不同來實(shí)現(xiàn)緩存是值得推薦的做法。 Jive 的緩存實(shí)現(xiàn)得比較簡單,可以用來學(xué)習(xí)和研究緩存機(jī)制。

Jive 中的 Cache 實(shí)現(xiàn)了緩存機(jī)制的大部分行為,它是將對象用惟一的關(guān)鍵字 Key 作標(biāo)識(shí)保存在 HashMap Hashtable 中。當(dāng)然,必須知道這些對象的大小,這個(gè)前提條件的設(shè)定可以保證緩存增長時(shí)不會(huì)超過規(guī)定的最大值。

如果緩存增長得太大,一些不經(jīng)常被訪問的對象將首先從緩存中刪除。如果設(shè)置了對象的最大生命周期時(shí)間,即使這個(gè)對象被反復(fù)頻繁訪問,也將從緩存中刪除。這個(gè)特性可以適用于一些周期性需要刷新的數(shù)據(jù),如來自數(shù)據(jù)庫的數(shù)據(jù)。

Cach 中除了 getObject() 方法的性能依據(jù)緩存大小,其他方法的性能都是比較快的。一個(gè) HashMap 用來實(shí)現(xiàn)快速尋找,兩個(gè) LinkedList 中一個(gè)以一定的訪問順序來保存對象,叫 accessed LinkedList ;另外一個(gè)以它們加入緩存的順序保存這些對象,這種保存對象只是保存對象的引用,叫 age LinkedList 。注意,這里的 LinkedList 不是 JDK 中的 LinkedList ,而是 Jive 自己定義的 LinkedList 。

當(dāng)對象被加入緩存時(shí),首先被 CacheObject 封裝。封裝有以下信息:對象大?。ㄒ宰止?jié)計(jì)算),一個(gè)指向 accessed LinkedList 的引用,一個(gè)指向 age LinkedList 的引用。

當(dāng)從緩存中獲取一個(gè)對象如 ObjectA 時(shí),首先, HashMap 尋找到指向封裝 ObjectA 等信息的 CacheObject 對象。然后,這個(gè)對象將被移動(dòng)到 accessed LinkedList 的前面,還有其他一些動(dòng)作如緩存清理、刪除、過期失效等都是在這個(gè)動(dòng)作中一起觸發(fā)實(shí)現(xiàn)的。

public class Cache implements Cacheable {

    /**

     * 因?yàn)?/span> System.currentTimeMillis() 執(zhí)行非常耗費(fèi)性能,因此如果 get 操作都執(zhí)行

* 這條語句將會(huì)形成性能瓶頸, 通過一個(gè)全局時(shí)間戳來實(shí)現(xiàn)每秒更新

* 當(dāng)然,這意味著在緩存過期時(shí)間計(jì)算上有一到幾秒的誤差

     */

    protected static long currentTime = CacheTimer.currentTime;

    //CacheObject 對象

    protected HashMap cachedObjectsHash;

    //accessed LinkedList 最經(jīng)常訪問的排列在最前面

    protected LinkedList lastAccessedList;

    // 以緩存加入順序排列,最后加入排在最前面;越早加入的排在最后面

    protected LinkedList ageList;

    // 緩存最大限制 默認(rèn)是 128k 可根據(jù)內(nèi)存設(shè)定,越大性能越高

    protected int maxSize =  128 * 1024;

    // 當(dāng)前緩存的大小

    protected int size = 0;

    // 最大生命周期時(shí)間,默認(rèn)是沒有

    protected long maxLifetime = -1;

    // 緩存的擊中率,用于評測緩存效率

    protected long cacheHits, cacheMisses = 0L;

 

    public Cache() {

        // 構(gòu)造 HashMap. 默認(rèn) capacity 11

        // 如果實(shí)際大小超過 11 , HashMap 將自動(dòng)擴(kuò)充,但是每次擴(kuò)充都

// 是性能開銷,因此期初要設(shè)置大一點(diǎn)

        cachedObjectsHash = new HashMap(103);

        lastAccessedList = new LinkedList();

        ageList = new LinkedList();

    }

    public Cache(int maxSize) {

        this();

        this.maxSize = maxSize;

    }

    public Cache(long maxLifetime) {

        this();

        this.maxLifetime = maxLifetime;

    }

    public Cache(int maxSize, long maxLifetime) {

        this();

        this.maxSize = maxSize;

        this.maxLifetime = maxLifetime;

    }

    public int getSize() {        return size;    }

    public int getMaxSize() {        return maxSize;    }

 

    public void setMaxSize(int maxSize) {

        this.maxSize = maxSize;

        // 有可能緩存大小超過最大值,需要激活刪除清理動(dòng)作

        cullCache();

    }

    public synchronized int getNumElements() {

        return cachedObjectsHash.size();

    }

 

    /**

     * 增加一個(gè) Cacheable 對象

* 因?yàn)?/span> HashMap 不是線程安全的,所以操作方法要使用同步

* 如果使用 Hashtable 就不必同步

     */

    public synchronized void add(Object key, Cacheable object) {

        // 刪除已經(jīng)存在的 key

        remove(key);

        int objectSize = object.getSize();

        // 如果被緩存對象的大小超過最大值,就放棄

        if (objectSize > maxSize * .90) {            return;        }

        size += objectSize;

        // 創(chuàng)建一個(gè) CacheObject 對象

        CacheObject cacheObject = new CacheObject(object, objectSize);

        cachedObjectsHash.put(key, cacheObject);  // 保存這個(gè) CacheObject

        // 加入 accessed LinkedList , Jive 自己的 LinkedList 在加入時(shí)可以返回值

        LinkedListNode lastAccessedNode = lastAccessedList.addFirst(key);

        // 保存引用

        cacheObject.lastAccessedListNode = lastAccessedNode;

        // 加入到 age LinkedList

        LinkedListNode ageNode = ageList.addFirst(key);

        // 這里直接調(diào)用 System.currentTimeMillis(); 用法值得討論

        ageNode.timestamp = System.currentTimeMillis();

        // 保存引用

        cacheObject.ageListNode = ageNode;

        // 做一些清理工作

        cullCache();

    }

    /**

     * 從緩存中獲得一個(gè)被緩存的對象,這個(gè)方法在下面兩種情況返回空

     *    <li> 該對象引用從來沒有被加入緩存中

     *    <li> 對象引用因?yàn)檫^期被清除 </ul>

     */

    public synchronized Cacheable get(Object key) {

        // 清除過期緩存

        deleteExpiredEntries();

        // Key 從緩存中獲取一個(gè)對象引用

        CacheObject cacheObject = (CacheObject)cachedObjectsHash.get(key);

        if (cacheObject == null) {

            // 不存在,增加未命中率

            cacheMisses++;

            return null;

        }

        // 存在,增加命中率

        cacheHits++;

        // accessed LinkedList 中將對象從當(dāng)前位置刪除

        // 重新插入在第一個(gè)

        cacheObject.lastAccessedListNode.remove();

        lastAccessedList.addFirst(cacheObject.lastAccessedListNode);

        return cacheObject.object;

    }

    …

}

Cache 中,關(guān)鍵字 Key 是一個(gè)對象,為了再次提高性能,可以進(jìn)一步將 Key 確定為一個(gè) long 類型的整數(shù)。

4.2  緩存使用

建立 LongCache 只是為了提高原來的 Cache 性能,本身無多大意義,可以將 LongCache 看成與 Cache 一樣的類。

LongCache 的關(guān)鍵字 Key Forum ForumThread 以及 ForumMessage long 類型的 ID ,值 Value Forum ForumThread 以及 ForumMessage 等的對象。這些基本是通過 DatabaseCacheManager 實(shí)現(xiàn)完成,在主要類 DbForumFactory 的初始化構(gòu)造時(shí),同時(shí)構(gòu)造了 DatabaseCacheManager 的實(shí)例 cacheManager

前面過濾器功能分析中, Message 對象獲得方法的第一句如下:

protected ForumMessage getMessage(long messageID, long threadID, long forumID) throws

      ForumMessageNotFoundException {

    DbForumMessage message = cacheManager.messageCache.get(messageID);

    …

}

其中, cacheManager DatabaseCacheManager 的實(shí)例, DatabaseCacheManager 是一個(gè)緩存 Facade 類。在其中包含了 5 種類型的緩存,都是針對 Jive 5 個(gè)主要對象, DatabaseCacheManager 主要代碼如下:

public class DatabaseCacheManager {

    public UserCache userCache;                          // 用戶資料緩存

    public GroupCache groupCache;                       // 組資料緩存

    public ForumCache forumCache;                       //Forum 論壇緩存

    public ForumThreadCache threadCache;                //Thread 主題緩存

    public ForumMessageCache messageCache;          //Message 緩存

    public UserPermissionsCache userPermsCache;     // 用戶權(quán)限緩存

 

    public DatabaseCacheManager(DbForumFactory factory) {

        …

        forumCache =

            new ForumCache(new LongCache(forumCacheSize, 6*HOUR), factory);

        threadCache =

            new ForumThreadCache(

                  new LongCache(threadCacheSize, 6*HOUR), factory);

        messageCache = new ForumMessageCache(

                  new LongCache(messageCacheSize, 6*HOUR), factory);

        userCache = new UserCache(

                  new LongCache(userCacheSize, 6*HOUR), factory);

        groupCache = new GroupCache(

                  new LongCache(groupCacheSize, 6*HOUR), factory);

        userPermsCache = new UserPermissionsCache(

                new UserPermsCache(userPermCacheSize, 24*HOUR), factory

        );

    }

    …

}

從以上代碼看出, ForumCache 等對象生成都是以 LongCache 為基礎(chǔ)構(gòu)建的,以 ForumCache 為例,代碼如下:

public class ForumCache extends DatabaseCache {

    // Cache 構(gòu)建 ID 緩存

    protected Cache forumIDCache = new Cache(128*1024, 6*JiveGlobals.HOUR);

    // LongCache 構(gòu)建整個(gè)對象緩存

    public ForumCache(LongCache cache, DbForumFactory forumFactory) {

        super(cache, forumFactory);

    }

 

    public DbForum get(long forumID) throws ForumNotFoundException {

        …

        DbForum forum = (DbForum)cache.get(forumID);

        if (forum == null) {    // 如果緩存沒有從數(shù)據(jù)庫中獲取

            forum = new DbForum(forumID, factory);

            cache.add(forumID, forum);

        }

        return forum;

    }

 

public Forum get(String name) throws ForumNotFoundException {

         // name key ,從 forumIDCache 中獲取 ID

 CacheableLong forumIDLong = (CacheableLong)forumIDCache.get(name);

        if (forumIDLong == null) { // 如果緩存沒有 從數(shù)據(jù)庫獲得

            long forumID = factory.getForumID(name);

            forumIDLong = new CacheableLong(forumID); // 生成一個(gè)緩存對象

            forumIDCache.add(name, forumIDLong);

        }

        return get(forumIDLong.getLong());

    }

    …

}

由此可以看到, LongCache 封裝了 Cache 的核心功能,而 ForumCache 等類則是在 LongCache 核心外又包裝了與應(yīng)用系統(tǒng)相關(guān)的操作,這有點(diǎn)類似裝飾( Decorator )模式。

從中也可以看到 Cache LongCache 兩種緩存的用法。

使用 Cache 時(shí)的關(guān)鍵字 Key 是任何字段。如上面代碼中的 String name ,如果用戶大量帖子主題查詢中, Key query + blockID ,見 DbForum 中的 getThreadBlock 方法;而值 Value 則是 Long 類型的 ID ,如 ForumID ThreadID 等。

LongCache 的關(guān)鍵字 Key Long 類型的 ID ,如 ForumID ThreadID 等;而值 Value 則是 Forum ForumThread ForumMessage 等主要具體對象。

在實(shí)際使用中,大多數(shù)是根據(jù) ID 獲得對象。但有時(shí)并不是這樣,因此根據(jù)應(yīng)用區(qū)分了兩種 Cache ,這其實(shí)類似數(shù)據(jù)庫的數(shù)據(jù)表,除了主關(guān)鍵字外還有其他關(guān)鍵字。

4.3  小結(jié)

緩存中對象是原對象的映射,如何確保緩存中對象和原對象的一致性?即當(dāng)原對象發(fā)生變化時(shí),緩存中的對象也必須立即更新。這是緩存機(jī)制需要解決的另外一個(gè)基本技術(shù)問題。

Jive 中是在原對象發(fā)生變化時(shí),立即進(jìn)行清除緩存中對象,如 ForumMessage 對象的創(chuàng)建。在 DbForumThread AddMessage 方法中有下列語句:

factory.cacheManager.threadCache.remove(this.id);

factory.cacheManager.forumCache.remove(this.forumID);

即當(dāng)有新的帖子加入時(shí),將 ForumThreadCache ForumCache 相關(guān)緩沖全部清除。這樣,當(dāng)有相關(guān)對象讀取時(shí),將直接從數(shù)據(jù)庫中讀取,這是一種非常簡單的緩存更新方式。

在復(fù)雜的系統(tǒng),例如有一臺(tái)以上的服務(wù)器運(yùn)行著 Jive 系統(tǒng)。如果一個(gè)用戶登陸一臺(tái)服務(wù)器后,通過這臺(tái)服務(wù)器增加新帖。那么按照上述原理,只能更新本服務(wù)器 JVM 中的緩存數(shù)據(jù),而其他服務(wù)器則無從得知這種改變,這就需要一種分布式的緩存機(jī)制。

3-7  Jive 主要對象的訪問

到目前可以發(fā)現(xiàn) , 整個(gè) Jive 系統(tǒng)其實(shí)是圍繞 Forum 、 ForumThread ForumMessage 等這些主要對象展開的讀取、修改或創(chuàng)建等操作。由于這些對象原先持久化保存在數(shù)據(jù)庫中,為了提高性能和加強(qiáng)安全性, Jive 在這些對象外面分別實(shí)現(xiàn)兩層包裝,如圖 3-7 所示。

客戶端如果需要訪問這些對象,首先要經(jīng)過它們的代理對象。進(jìn)行訪問權(quán)限的檢查,然后再從緩存中獲取該對象。只有緩存不存在時(shí),才會(huì)從數(shù)據(jù)庫中獲取。

這套機(jī)制是大多數(shù)應(yīng)用系統(tǒng)都面臨的必須解決的基本功能,因此完全可以做成一個(gè)通用的可重復(fù)使用的框架。這樣在具體應(yīng)用時(shí),不必每個(gè)應(yīng)用系統(tǒng)都架設(shè)開發(fā)這樣的機(jī)制。其實(shí) EJB 就是這樣一套框架,實(shí)體 Bean 都由緩存機(jī)制支持,而通過設(shè)定 ejb-jar.xml 可以實(shí)現(xiàn)訪問權(quán)限控制,這些工作都直接由 EJB 容器實(shí)現(xiàn)了,不必在代碼中自己來實(shí)現(xiàn)。剩余的工作是調(diào)整 EJB 容器的參數(shù),使之適合應(yīng)用系統(tǒng)的具體要求,這些將在以后章節(jié)中討論。

Jive 中,圖 3-7 的機(jī)制是通過不同方式實(shí)現(xiàn)的?;旧鲜且慌涠J剑阂粋€(gè)對象有一個(gè)緩沖對象和一個(gè)代理對象,這樣做的一個(gè)缺點(diǎn)是導(dǎo)致對象太多,系統(tǒng)變得復(fù)雜。這點(diǎn)在閱讀 Jive 源碼時(shí)可能已經(jīng)發(fā)現(xiàn)。

如果建立一個(gè)對象工廠,工廠內(nèi)部封裝了圖 3-7 機(jī)制實(shí)現(xiàn)過程,客戶端可以根據(jù)不同的工廠輸入?yún)?shù)獲得具體不同的對象。這樣也許代碼結(jié)構(gòu)要更加抽象和緊湊, Java 的動(dòng)態(tài)代理 API 也許是實(shí)現(xiàn)這個(gè)工廠的主要技術(shù)基礎(chǔ)。有興趣者可以進(jìn)一步研究提煉。

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多