日韩黑丝制服一区视频播放|日韩欧美人妻丝袜视频在线观看|九九影院一级蜜桃|亚洲中文在线导航|青草草视频在线观看|婷婷五月色伊人网站|日本一区二区在线|国产AV一二三四区毛片|正在播放久草视频|亚洲色图精品一区

分享

Jive 的緩存機(jī)制

 bluecrystal 2007-07-21

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

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

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

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

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

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

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

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

public class Cache implements Cacheable {

    /**

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

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

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

     */

    protected static long currentTime = CacheTimer.currentTime;

    //CacheObject 對(duì)象

    protected HashMap cachedObjectsHash;

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

    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)是沒(méi)有

    protected long maxLifetime = -1;

    // 緩存的擊中率,用于評(píng)測(cè)緩存效率

    protected long cacheHits, cacheMisses = 0L;

 

    public Cache() {

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

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

// 是性能開(kāi)銷(xiāo),因此期初要設(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;

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

        cullCache();

    }

    public synchronized int getNumElements() {

        return cachedObjectsHash.size();

    }

 

    /**

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

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

* 如果使用 Hashtable 就不必同步

     */

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

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

        remove(key);

        int objectSize = object.getSize();

        // 如果被緩存對(duì)象的大小超過(guò)最大值,就放棄

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

        size += objectSize;

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

        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è)被緩存的對(duì)象,這個(gè)方法在下面兩種情況返回空

     *    <li> 該對(duì)象引用從來(lái)沒(méi)有被加入緩存中

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

     */

    public synchronized Cacheable get(Object key) {

        // 清除過(guò)期緩存

        deleteExpiredEntries();

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

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

        if (cacheObject == null) {

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

            cacheMisses++;

            return null;

        }

        // 存在,增加命中率

        cacheHits++;

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

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

        cacheObject.lastAccessedListNode.remove();

        lastAccessedList.addFirst(cacheObject.lastAccessedListNode);

        return cacheObject.object;

    }

    …

}

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

4.2  緩存使用

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

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

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

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

      ForumMessageNotFoundException {

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

    …

}

其中, cacheManager DatabaseCacheManager 的實(shí)例, DatabaseCacheManager 是一個(gè)緩存 Facade 類(lèi)。在其中包含了 5 種類(lèi)型的緩存,都是針對(duì) Jive 5 個(gè)主要對(duì)象, 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 等對(duì)象生成都是以 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è)對(duì)象緩存

    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) {    // 如果緩存沒(méi)有從數(shù)據(jù)庫(kù)中獲取

            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) { // 如果緩存沒(méi)有 從數(shù)據(jù)庫(kù)獲得

            long forumID = factory.getForumID(name);

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

            forumIDCache.add(name, forumIDLong);

        }

        return get(forumIDLong.getLong());

    }

    …

}

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

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

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

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

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

4.3  小結(jié)

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

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

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

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

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

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

3-7  Jive 主要對(duì)象的訪問(wèn)

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

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

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

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

如果建立一個(gè)對(duì)象工廠,工廠內(nèi)部封裝了圖 3-7 機(jī)制實(shí)現(xiàn)過(guò)程,客戶端可以根據(jù)不同的工廠輸入?yún)?shù)獲得具體不同的對(duì)象。這樣也許代碼結(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)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購(gòu)買(mǎi)等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類(lèi)似文章 更多