JetCache埋點的騷操作,不服不行啊

猿天地發表於2020-08-17

闡述背景

快取是應對高併發絕對的利器,在很多業務場景允許的情況下,都可以使用快取來提供效能。

既然用了快取,那對快取進行監控必不可少。比如快取載入耗時,新增耗時等。

在 JetCache 中進行埋點操作,對於 Redis 的快取沒有問題,埋點之後的 Key 是完整的,完整的也就是 Cache 的 name+key,如下圖:

除了對 Redis 的快取做埋點,還對本地 快取 Caffeine 也做了埋點操作,然後發現 Caffeine 的埋點有問題,問題在於 Cache 的 name 丟失了,如下圖:

然後去官方的釘釘群了問了下維護人員,讓我升級版本。然後我升級到最新的 2.6.0 還是不行,這不是坑我麼 ?!

找出原因

正所謂自己動手,豐衣足食。直接看程式碼吧,首先看 Redis 為何 Cache name 沒有丟失,原因是 Redis 的 Config 中有 keyPrefix,如下圖:

然後在對 Redis 進行操作的時候,會構建快取的 Key,構建 Key 的時候會帶上 keyPrefix,所以 Redis 的 Key 是正常的。

com.alicp.jetcache.external.AbstractExternalCache

public byte[] buildKey(K key) {
    try {
        Object newKey = key;
        if (key instanceof byte[]) {
            newKey = key;
        } else if (key instanceof String) {
            newKey = key;
        } else if (this.config.getKeyConvertor() != null) {
            newKey = this.config.getKeyConvertor().apply(key);
        }
        return ExternalKeyUtil.buildKeyAfterConvert(newKey, this.config.getKeyPrefix());
    } catch (IOException var3) {
        throw new CacheException(var3);
    }
}

然後來看 Caffeine 的 config 中是沒有 Keyprefix 的,如下圖:

然後在構建快取 Key 的時候,也就是沒有 Keyprefix,所以問題就出在這裡。

com.alicp.jetcache.embedded.AbstractEmbeddedCache

public Object buildKey(K key) {
    if (key == null) {
        return null;
    } else {
        Object newKey = key;
        Function<K, Object> keyConvertor = this.config.getKeyConvertor();
        if (keyConvertor != null) {
            newKey = keyConvertor.apply(key);
        }
        return newKey;
    }
}

RedisCacheConfig 繼承了 ExternalCacheConfig,ExternalCacheConfig 繼承了 CacheConfig。

keyPrefix 定義在 ExternalCacheConfig 中。

而 EmbeddedCacheConfig 只繼承了 CacheConfig,所以它自然就沒有 keyPrefix 欄位。

解決方案

原因找出來了,想要解決肯定是可以的。問題是這是個開源的框架,不是自己公司內部的程式碼。不過也可以直接將原始碼克隆下來,進行改造,然後打包釋出到自己的私服中去就可以了。

將 EmbeddedCacheConfig 也繼承 ExternalCacheConfig 就可以將 keyPrefix 透傳下去,然後在 buildKey 的地方進行拼接。

還有一種比較投機取巧的方案,可以不用改變配置類的關係,在 config 中有 monitors 這個資訊,裡面存放的是快取的監控資訊,主要是記錄快取對應的操作型別,GET, PUT 這種,然後就是每個操作的執行時間,操作次數等一些統計的資訊,最終會有一個執行緒定時將這些資訊輸出到日誌中。

所以我們可以通過獲取 monitors 中的 cacheName 來臨時解決這個問題。

private String getCacheName() {
    List<CacheMonitor> monitors = config.getMonitors();
    if (CollectionUtils.isEmpty(monitors)) {
        return "";
    }
    DefaultCacheMonitor cacheMonitor = (DefaultCacheMonitor) monitors.get(0);
    String cacheName = cacheMonitor.getCacheName();
    return cacheName;
}

功能程式碼: https://github.com/yinjihuan/kitty

關於作者 :尹吉歡,簡單的技術愛好者,《Spring Cloud 微服務-全棧技術與案例解析》, 《Spring Cloud 微服務 入門 實戰與進階》作者, 公眾號 猿天地 發起人。個人微信 jihuan900 ,歡迎勾搭。

我整理了一份很全的學習資料,感興趣的可以微信搜尋 「猿天地 」,回覆關鍵字 「學習資料 」獲取我整理好了的Spring Cloud,Spring Cloud Alibaba,Sharding-JDBC分庫分表,任務排程框架XXL-JOB,MongoDB,爬蟲等相關資料。

相關文章