微分享-高并發(fā)下的緩存實戰(zhàn)

場景

統(tǒng)計一個批量接口會有多少數(shù)據(jù)涉枫,這個接口的QPS在100萬級別孽鸡。有幾種方案:

  1. 每次調(diào)用都串行計算一次哪轿;
  2. 每次調(diào)用使用線程池并行計算盈魁。

由于并發(fā)量特別的大,第1種場景肯定不適合窃诉,這會把相應(yīng)時間拉長杨耙。第二種方法每次請求過來都放到一個線程池里面請求,比第一種強很多飘痛,用這種方式基本上可以解決80%左右的需求了珊膜。那么還有能優(yōu)化的地方么?答案是有的宣脉。

Cache + 線程池

一般在大的公司都有一些監(jiān)控系統(tǒng)车柠,可以將監(jiān)控的數(shù)據(jù)上報到監(jiān)控系統(tǒng)中。上面兩個場景都是每次請求都會調(diào)用上報接口塑猖,這樣特別浪費資源也可能出現(xiàn)性能問題竹祷。是否可以想一個辦法減少上報次數(shù)呢?我們可以使用cache匯總在一起羊苟,打包通過線程池異步上報塑陵。是不是這種方式會更好一些。

實現(xiàn)

怎么實現(xiàn)呢蜡励? 首先我們需要一個cache令花,這次我們使用Guava Cache。

Guava Cache 是google開發(fā)開源項目Guava中帶有的功能凉倚,只提供堆緩存兼都,也就是說重啟機器后就沒有了,特點:小巧玲瓏稽寒,性能最好扮碧。

private volatile static Cache<String, MutableInt> metricCache = null;


public static Cache<String, MutableInt> getMetricCache(){
    if (metricCache == null) {
        synchronized (this) {
            if (metricCache == null) {
                metricCache = initMetricCache();
                return metricCache;
            }
        }
    }
    return metricCache;
}

private static Cache<String, MutableInt> initMetricCache(){

    Cache<String, MutableInt> initMetricCache = CacheBuilder.newBuilder()
            // 設(shè)置緩存?zhèn)€數(shù)
            .maximumSize(1024)
            // 設(shè)置cache中的數(shù)據(jù)在寫入之后的存活時間為1秒
            .expireAfterWrite(1, TimeUnit.MINUTES)
            // 設(shè)置并發(fā)數(shù)為8,即同一時間最多只能有5個線程往cache執(zhí)行寫入操作 
            .concurrencyLevel(8)
            // 聲明一個監(jiān)聽器瓦胎,緩存項被移除時做一些額外操作芬萍。這里使用異步線程池的形式實現(xiàn),更加高效搔啊。
            .removalListener(RemovalListeners.asynchronous(new RemovalListener<String, MutableInt>(){
                @Override
                public void onRemoval(RemovalNotification<String, MutableInt> notification) {
                    // 刪除后的邏輯操作柬祠,這里是上報到監(jiān)控系統(tǒng)中 
                    metricForCount(notification.getKey(), notification.getValue().intValue());
                }
            },
            // 自定義線程池,這里就不在把實現(xiàn)的代碼粘進來了 
            taskExecutor.getTaskExecutor()))
            .build();

    return initMetricCache;
}

對上面的代碼進行分析:

  • CacheBuilder.newBuilder()創(chuàng)建一個Guava Cache负芋,設(shè)置一些配置;
  • 在調(diào)用時考慮到高效性漫蛔,使用了一個小技巧延遲加載嗜愈,參考getMetricCache()實現(xiàn);
  • 在Guava Cache中使用removalListener特性莽龟,結(jié)合我們的需求蠕嫁,當統(tǒng)計記錄達到一定的數(shù)量后,刪除掉并在監(jiān)聽的線程池中實現(xiàn)上報毯盈。

應(yīng)用

看著很牛B剃毒,怎么使用呢?

    public static void logMetricForCount(final String key, final int count) {

        try {
            MutableInt logMetric = getMetricCache().get(key, new Callable<MutableInt>() {
                @Override
                public MutableInt call() throws Exception {
                    return new MutableInt(0);
                }
            });

            // 計數(shù)
            logMetric.add(count);
            if(logMetric.intValue() > 500){
                // 當計數(shù)達到500個時刪除此key搂赋,從而觸發(fā)上面配置的removalListener
                getMetricCache().invalidate(key);
            }
        } catch (Exception e) {
            logger.warn("統(tǒng)計{}信息次數(shù){}異常", key, count, e);
        }
    }

在實戰(zhàn)的計數(shù)操作赘阀,apache提供了MutableInt專門用于高效計數(shù)的類。還使用到Guava Cache的特性脑奠。

MutableInt logMetric = getMetricCache().get(key, new Callable<MutableInt>() {
                @Override
                public MutableInt call() throws Exception {
                    return new MutableInt(0);
                }
            });

當沒有g(shù)et到數(shù)據(jù)時基公,自動初始化一個。是不是很棒宋欺!
代碼是不是就到此結(jié)束了轰豆? 不是的。我們在開發(fā)代碼時需要考慮高效齿诞。Guava Cache在設(shè)計時也考慮到高效性酸休,不過如果不仔細閱讀使用文檔,也會給自己買坑掌挚。

Guava Cache清理什么時候發(fā)生雨席?使用CacheBuilder構(gòu)建的緩存不會"自動"執(zhí)行清理和回收工作,也不會在某個緩存項過期后馬上清理吠式,也沒有諸如此類的清理機制陡厘。相反,它會在寫操作時順帶做少量的維護工作特占,或者偶爾在讀操作時做——如果寫操作實在太少的話糙置。
如果你的緩存是高吞吐的,那就無需擔心緩存的維護和清理等工作是目。如果你的 緩存只會偶爾有寫操作谤饭,而你又不想清理工作阻礙了讀操作,那么可以創(chuàng)建自己的維護線程懊纳,以固定的時間間隔調(diào)用Cache.cleanUp()揉抵。ScheduledExecutorService可以幫助你很好地實現(xiàn)這樣的定時調(diào)度。

對于高并發(fā)量的情況下嗤疯,我們還需要寫一個線程去定時cleanUp冤今。

Runnable metrciCacheCleanUpTask = new Runnable() {
    @Override
    public void run() {
                getMetricCache().cleanUp();
            } catch (Exception e) {
            logger.error("定時cleanUp方法異常",e);
        }
    }
};
// 使用線程池每分鐘執(zhí)行一次
commTaskScheduler.scheduleWithFixedDelay(metrciCacheCleanUpTask, 60000);

線程池相關(guān)的實現(xiàn)可以參考我以前的blog,微分享-spring線程池實戰(zhàn)

Guava Cache CacheLoader還提供了數(shù)據(jù)加載機制茂缚,有興趣的話可以研究一下戏罢。

參考:
[Google Guava] 3-緩存

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末屋谭,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子龟糕,更是在濱河造成了極大的恐慌桐磁,老刑警劉巖,帶你破解...
    沈念sama閱讀 207,113評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件讲岁,死亡現(xiàn)場離奇詭異我擂,居然都是意外死亡,警方通過查閱死者的電腦和手機催首,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,644評論 2 381
  • 文/潘曉璐 我一進店門扶踊,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人郎任,你說我怎么就攤上這事”缸眩” “怎么了舶治?”我有些...
    開封第一講書人閱讀 153,340評論 0 344
  • 文/不壞的土叔 我叫張陵,是天一觀的道長车猬。 經(jīng)常有香客問我霉猛,道長,這世上最難降的妖魔是什么珠闰? 我笑而不...
    開封第一講書人閱讀 55,449評論 1 279
  • 正文 為了忘掉前任惜浅,我火速辦了婚禮,結(jié)果婚禮上伏嗜,老公的妹妹穿的比我還像新娘坛悉。我一直安慰自己,他們只是感情好承绸,可當我...
    茶點故事閱讀 64,445評論 5 374
  • 文/花漫 我一把揭開白布裸影。 她就那樣靜靜地躺著,像睡著了一般军熏。 火紅的嫁衣襯著肌膚如雪轩猩。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,166評論 1 284
  • 那天荡澎,我揣著相機與錄音均践,去河邊找鬼。 笑死摩幔,一個胖子當著我的面吹牛彤委,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播热鞍,決...
    沈念sama閱讀 38,442評論 3 401
  • 文/蒼蘭香墨 我猛地睜開眼葫慎,長吁一口氣:“原來是場噩夢啊……” “哼衔彻!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起偷办,我...
    開封第一講書人閱讀 37,105評論 0 261
  • 序言:老撾萬榮一對情侶失蹤艰额,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后椒涯,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體柄沮,經(jīng)...
    沈念sama閱讀 43,601評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,066評論 2 325
  • 正文 我和宋清朗相戀三年废岂,在試婚紗的時候發(fā)現(xiàn)自己被綠了祖搓。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,161評論 1 334
  • 序言:一個原本活蹦亂跳的男人離奇死亡湖苞,死狀恐怖拯欧,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情财骨,我是刑警寧澤镐作,帶...
    沈念sama閱讀 33,792評論 4 323
  • 正文 年R本政府宣布,位于F島的核電站隆箩,受9級特大地震影響该贾,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜捌臊,卻給世界環(huán)境...
    茶點故事閱讀 39,351評論 3 307
  • 文/蒙蒙 一杨蛋、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧理澎,春花似錦逞力、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,352評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至秩铆,卻和暖如春砚亭,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背殴玛。 一陣腳步聲響...
    開封第一講書人閱讀 31,584評論 1 261
  • 我被黑心中介騙來泰國打工捅膘, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人滚粟。 一個月前我還...
    沈念sama閱讀 45,618評論 2 355
  • 正文 我出身青樓寻仗,卻偏偏與公主長得像,于是被迫代替她去往敵國和親凡壤。 傳聞我的和親對象是個殘疾皇子署尤,可洞房花燭夜當晚...
    茶點故事閱讀 42,916評論 2 344

推薦閱讀更多精彩內(nèi)容