監(jiān)控Redis操作

目的:監(jiān)控Redis操作的執(zhí)行時(shí)間腻窒、執(zhí)行命令栅表、執(zhí)行參數(shù)和執(zhí)行結(jié)果

監(jiān)控操作時(shí)間基本邏輯

//開始統(tǒng)計(jì)
 //do something
//結(jié)束統(tǒng)計(jì)

RedisTemplate核心執(zhí)行方法

public <T> T execute(RedisCallback<T> action, boolean exposeConnection, boolean pipeline){
        //邏輯代碼
}

所有的方法調(diào)用最終都會(huì)執(zhí)行到這個(gè)方法上

而這個(gè)方法會(huì)獲取到與Redis的連接對(duì)象,將這個(gè)對(duì)象傳到回調(diào)函數(shù)中翰意,在回調(diào)函數(shù)中調(diào)用Redis。

所以可以在這個(gè)方法的執(zhí)行前后增加埋點(diǎn)操作,統(tǒng)計(jì)redis執(zhí)行時(shí)間

如繼承RedisTemplate類条辟,重寫execute方法,就可以在redis操作執(zhí)行前后統(tǒng)計(jì)執(zhí)行時(shí)間

 @Override
public <T> T execute(RedisCallback<T> action, boolean exposeConnection, boolean pipeline){
        //開始統(tǒng)計(jì)
        super.execute(action,exposeConnection,pipeline);
        //結(jié)束統(tǒng)計(jì)
        //統(tǒng)計(jì)結(jié)果
}

但是excute方法的參數(shù)只有一個(gè)回調(diào)函數(shù)和兩個(gè)判斷條件宏胯,具體在回調(diào)函數(shù)調(diào)用的是redis的哪個(gè)操作羽嫡,是set還是delete等都無法知道,無法針對(duì)性的統(tǒng)計(jì)每個(gè)具體方法的執(zhí)行時(shí)間肩袍,無法獲取到相關(guān)的執(zhí)行參數(shù)杭棵,比如具體執(zhí)行了哪個(gè)方法、執(zhí)行的參數(shù)是哪些等氛赐。

所以閱讀execute方法的執(zhí)行邏輯魂爪,發(fā)現(xiàn)RedisTemplate在執(zhí)行回調(diào)函數(shù)的前后,給子類留了功能擴(kuò)展的方法艰管,很明顯是為了能夠在操作執(zhí)行前后能夠執(zhí)行相關(guān)的擴(kuò)展功能的操作滓侍,關(guān)鍵代碼如下

public <T> T execute(RedisCallback<T> action, boolean exposeConnection, boolean pipeline) {
        //從工廠中獲取連接    

        this.preProcessConnection(conn, existingConnection);   //執(zhí)行回調(diào)函數(shù)方法前
        Object result = action.doInRedis(connToExpose); //執(zhí)行回調(diào)函數(shù)方法
        this.postProcessResult(result, connToUse, existingConnection); //執(zhí)行回調(diào)函數(shù)方法后

        //釋放連接        
}

protected RedisConnection preProcessConnection(RedisConnection connection, boolean existingConnection) {
        return connection;
}

protected <T> T postProcessResult(T result, RedisConnection conn, boolean existingConnection) {
        return result;
}

可以看到preProcessConnection和postProcessResult這兩個(gè)方法,在RedisTemplate中都是留空的牲芋,并且方法類型都是protected撩笆,而且執(zhí)行的順序分別是在action.doInRedis()這個(gè)回調(diào)函數(shù)前后,所以很明顯是為了留給子類繼承缸浦,來進(jìn)行功能擴(kuò)展夕冲。所以,我們可以繼承這兩個(gè)方法裂逐,在這兩個(gè)方法中對(duì)Redis執(zhí)行時(shí)間進(jìn)行埋點(diǎn)統(tǒng)計(jì)歹鱼。

protected RedisConnection preProcessConnection(RedisConnection connection, boolean existingConnection) {
        //開始統(tǒng)計(jì)
        return connection;
}

protected <T> T postProcessResult(T result, RedisConnection conn, boolean existingConnection) {
        //結(jié)束統(tǒng)計(jì)
        //記錄執(zhí)行結(jié)果
        return result;
}

但是這種方法同樣會(huì)出現(xiàn)上一種方法的問題,無法獲取到執(zhí)行的具體方法卜高、參數(shù)醉冤。因此,繼續(xù)單步至回調(diào)函數(shù)中篙悯,查看回調(diào)函數(shù)的執(zhí)行過程能否有機(jī)會(huì)進(jìn)行埋點(diǎn)蚁阳。對(duì)常用的redisTemplate.opsForValue().get()方法進(jìn)行跟蹤

public ValueOperations<K, V> opsForValue() {
    if(this.valueOps == null) {
        this.valueOps = new DefaultValueOperations(this);
    }
    return this.valueOps;
}

public V get(final Object key) {
    return this.execute(new ValueDeserializingRedisCallback(this, key) {
         protected byte[] inRedis(byte[] rawKey, RedisConnection connection) {
             return connection.get(rawKey);
         }
     }, true);
}

abstract class ValueDeserializingRedisCallback implements RedisCallback<V> {
    private Object key;
    public ValueDeserializingRedisCallback(Object key) {
        this.key = key;
    }
    public final V doInRedis(RedisConnection connection) {
        byte[] result = this.inRedis(AbstractOperations.this.rawKey(this.key), connection);
        return AbstractOperations.this.deserializeValue(result);
    }

    protected abstract byte[] inRedis(byte[] var1, RedisConnection var2);
}

可以看到,這個(gè)方法最終的確是調(diào)用了redisTemplate的exeute方法鸽照,并且是在回調(diào)函數(shù)中調(diào)用RedisConnection的get方法來獲取redis的數(shù)據(jù)螺捐。所以我們需要對(duì)connection.set(rawKey, rawValue)這個(gè)方法進(jìn)行攔截,就可以知道執(zhí)行的參數(shù)、執(zhí)行的結(jié)果定血,更進(jìn)一步的思考赔癌,如果知道調(diào)用的是RedisConnection哪個(gè)方法,就可以知道在redis中澜沟,執(zhí)行的是那一個(gè)redis命令灾票。

//開始統(tǒng)計(jì)
//獲取參數(shù)
return connection.get(rawKey); //獲取命令
//結(jié)束統(tǒng)計(jì)
//獲取結(jié)果

查看RedisConnection的代碼,發(fā)現(xiàn)它是一個(gè)接口茫虽,繼承RedisCommands刊苍,而RedisCommands繼承了各種與Redis相關(guān)的命令接口。因此濒析,知道知道在調(diào)用RedisConnection的時(shí)候正什,調(diào)用的是哪一個(gè)接口,就可以知道執(zhí)行的是redis的哪一個(gè)命令号杏。并且婴氮,由于我們不可能修改RediTemplate的源碼,來在每一個(gè)操作的地方進(jìn)行埋點(diǎn)盾致,所以我們就可以從RedisConnection這個(gè)對(duì)象進(jìn)行處理主经,在RedisConnection的方法執(zhí)行前后,進(jìn)行攔截庭惜,于是可以考慮用動(dòng)態(tài)代理的方式旨怠,攔截RedisConnection對(duì)象的方法,在執(zhí)行方法的前后進(jìn)行埋點(diǎn)統(tǒng)計(jì)工作蜈块。于是我們可以繼承RedisTemplate的preProcessConnection函數(shù)鉴腻,對(duì)RedisConnection進(jìn)行動(dòng)態(tài)代理并返回代理后的RedisConnection對(duì)象,這樣實(shí)際執(zhí)行的時(shí)候百揭,doInRedis這個(gè)回調(diào)函數(shù)中執(zhí)行的RedisConnection就是我們動(dòng)態(tài)代理后的對(duì)象爽哎。

@Override
protected RedisConnection preProcessConnection(RedisConnection connection, boolean existingConnection) {
    //對(duì)Connection對(duì)象進(jìn)行動(dòng)態(tài)代理
    Class[] interfacesForClass = ClassUtils.getAllInterfacesForClass(connection.getClass(), this.getClass().getClassLoader());
        connection = (RedisConnection)         
    Proxy.newProxyInstance(connection.getClass().getClassLoader(),interfacesForClass,new MyRedisConnection(connection,this)) ;
    return super.preProcessConnection(connection, existingConnection);
}

private class MyRedisConnection implements InvocationHandler{
        private final RedisConnection target ;
        protected CatRedisConnection(RedisConnection target){
            this.target = target ;
        }

        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            //開始統(tǒng)計(jì)
            result = method.invoke(this.target,args) ;
            //結(jié)束統(tǒng)計(jì)
            //獲取redis命令、參數(shù)器一、結(jié)果
            return result;
        }
    }

這樣就實(shí)現(xiàn)了對(duì)Redis操作的埋點(diǎn)课锌,能夠統(tǒng)計(jì)每一個(gè)Redis操作的執(zhí)行時(shí)間、執(zhí)行命令祈秕、執(zhí)行參數(shù)和執(zhí)行結(jié)果渺贤。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市请毛,隨后出現(xiàn)的幾起案子志鞍,更是在濱河造成了極大的恐慌,老刑警劉巖方仿,帶你破解...
    沈念sama閱讀 216,692評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件固棚,死亡現(xiàn)場(chǎng)離奇詭異统翩,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)此洲,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,482評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門厂汗,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人呜师,你說我怎么就攤上這事娶桦。” “怎么了汁汗?”我有些...
    開封第一講書人閱讀 162,995評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵衷畦,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我碰酝,道長(zhǎng),這世上最難降的妖魔是什么戴差? 我笑而不...
    開封第一講書人閱讀 58,223評(píng)論 1 292
  • 正文 為了忘掉前任送爸,我火速辦了婚禮,結(jié)果婚禮上暖释,老公的妹妹穿的比我還像新娘袭厂。我一直安慰自己,他們只是感情好球匕,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,245評(píng)論 6 388
  • 文/花漫 我一把揭開白布纹磺。 她就那樣靜靜地躺著,像睡著了一般亮曹。 火紅的嫁衣襯著肌膚如雪橄杨。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,208評(píng)論 1 299
  • 那天照卦,我揣著相機(jī)與錄音式矫,去河邊找鬼。 笑死役耕,一個(gè)胖子當(dāng)著我的面吹牛采转,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播瞬痘,決...
    沈念sama閱讀 40,091評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼故慈,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了框全?” 一聲冷哼從身側(cè)響起察绷,我...
    開封第一講書人閱讀 38,929評(píng)論 0 274
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎津辩,沒想到半個(gè)月后克婶,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體筒严,經(jīng)...
    沈念sama閱讀 45,346評(píng)論 1 311
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,570評(píng)論 2 333
  • 正文 我和宋清朗相戀三年情萤,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了鸭蛙。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,739評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡筋岛,死狀恐怖娶视,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情睁宰,我是刑警寧澤肪获,帶...
    沈念sama閱讀 35,437評(píng)論 5 344
  • 正文 年R本政府宣布,位于F島的核電站柒傻,受9級(jí)特大地震影響孝赫,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜红符,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,037評(píng)論 3 326
  • 文/蒙蒙 一青柄、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧预侯,春花似錦致开、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,677評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至糜芳,卻和暖如春飒货,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背峭竣。 一陣腳步聲響...
    開封第一講書人閱讀 32,833評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工膏斤, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人邪驮。 一個(gè)月前我還...
    沈念sama閱讀 47,760評(píng)論 2 369
  • 正文 我出身青樓莫辨,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親毅访。 傳聞我的和親對(duì)象是個(gè)殘疾皇子沮榜,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,647評(píng)論 2 354

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

  • 1.ios高性能編程 (1).內(nèi)層 最小的內(nèi)層平均值和峰值(2).耗電量 高效的算法和數(shù)據(jù)結(jié)構(gòu)(3).初始化時(shí)...
    歐辰_OSR閱讀 29,373評(píng)論 8 265
  • 1+1=2 從一個(gè)人變成二個(gè)人 需要的時(shí)間,是你我的時(shí)間 是從我走到你那里 再?gòu)哪阕叩轿疫@里 經(jīng)歷的坎坷喻粹,始料未及...
    幻芷寧閱讀 554評(píng)論 0 1
  • 這一場(chǎng)危機(jī)山憨!有多少人知道? 最近我的公眾號(hào)后臺(tái)留言比較活躍弥喉,有感謝的郁竟,有鼓勵(lì)的,有給我加油的由境,但其中有一位朋友給我...
    竹筒歲月閱讀 270評(píng)論 0 2
  • 千萬億條山路水路我偏行這里遇見這畫展我駐足你面前觀看愛情和靈魂被攝進(jìn)畫里面像遇見一個(gè)宇宙而默默歡喜像無緣一段清風(fēng)而...
    梅朵0808閱讀 189評(píng)論 0 0
  • 班級(jí)情況: 校區(qū):科學(xué)創(chuàng)想樂高機(jī)器人和平校區(qū) 時(shí)間:周四17:00——19:00 學(xué)員: 劉軒孜 王啟赫 任教老...
    bong撒卡啦卡閱讀 376評(píng)論 0 0