自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

緩存框架 Caffeine 的可視化探索與實(shí)踐

開發(fā)
Caffeine 作為一個(gè)高性能的緩存框架而被大量使用。本文基于Caffeine已有的基礎(chǔ)進(jìn)行定制化開發(fā)實(shí)現(xiàn)可視化功能。

一、背景

Caffeine緩存是一個(gè)高性能、可擴(kuò)展、內(nèi)存優(yōu)化的 Java 緩存庫,基于 Google 的 Guava Cache演進(jìn)而來并提供了接近最佳的命中率。

Caffeine 緩存包含以下特點(diǎn)

  1. 高效快速:Caffeine 緩存使用近似算法和并發(fā)哈希表等優(yōu)化技術(shù),使得緩存的訪問速度非???。
  2. 內(nèi)存友好:Caffeine 緩存使用一種內(nèi)存優(yōu)化策略,能夠根據(jù)需要?jiǎng)討B(tài)調(diào)整緩存的大小,有效地利用內(nèi)存資源。
  3. 多種緩存策略:Caffeine 緩存支持多種緩存策略,如基于容量、時(shí)間、權(quán)重、手動(dòng)移除、定時(shí)刷新等,并提供了豐富的配置選項(xiàng),能夠適應(yīng)不同的應(yīng)用場景和需求。
  4. 支持異步加載和刷新:Caffeine 緩存支持異步加載和刷新緩存項(xiàng),可以與 Spring 等框架無縫集成。
  5. 清理策略:Caffeine 使用 Window TinyLFU 清理策略,它提供了接近最佳的命中率。
  6. 支持自動(dòng)加載和自動(dòng)過期:Caffeine 緩存可以根據(jù)配置自動(dòng)加載和過期緩存項(xiàng),無需手動(dòng)干預(yù)。
  7. 統(tǒng)計(jì)功能:Caffeine 緩存提供了豐富的統(tǒng)計(jì)功能,如緩存命中率、緩存項(xiàng)數(shù)量等,方便評估緩存的性能和效果。

正是因?yàn)镃affeine具備的上述特性,Caffeine作為項(xiàng)目中本地緩存的不二選擇,越來越多的項(xiàng)目集成了Caffeine的功能,進(jìn)而衍生了一系列的業(yè)務(wù)視角的需求。

日常使用的需求之一希望能夠?qū)崟r(shí)評估Caffeine實(shí)例的內(nèi)存占用情況并能夠提供動(dòng)態(tài)調(diào)整緩存參數(shù)的能力,但是已有的內(nèi)存分析工具M(jìn)AT需要基于dump的文件進(jìn)行分析無法做到實(shí)時(shí),這也是整個(gè)事情的起因之一。

二、業(yè)務(wù)的技術(shù)視角

  • 能夠?qū)?xiàng)目中的Caffeine的緩存實(shí)例能夠做到近實(shí)時(shí)統(tǒng)計(jì),實(shí)時(shí)查看緩存的實(shí)例個(gè)數(shù)。
  • 能夠?qū)affeine的每個(gè)實(shí)例的緩存配置參數(shù)、內(nèi)存占用、緩存命中率做到實(shí)時(shí)查看,同時(shí)能夠支持單個(gè)實(shí)例的緩存過期時(shí)間,緩存條目等參數(shù)進(jìn)行動(dòng)態(tài)配置下發(fā)。
  • 能夠?qū)affeine的每個(gè)實(shí)例的緩存數(shù)據(jù)做到實(shí)時(shí)查看,并且能夠支持緩存數(shù)據(jù)的立即失效等功能。

基于上述的需求背景,結(jié)合caffeine的已有功能和定制的部分源碼開發(fā),整體作為caffeine可視化的技術(shù)項(xiàng)目進(jìn)行推進(jìn)和落地。

三、可視化能力

Caffeine可視化項(xiàng)目目前已支持功能包括:

  • 項(xiàng)目維度的全局緩存實(shí)例的管控。
  • 單緩存實(shí)例配置信息可視化、內(nèi)存占用可視化、命中率可視化。
  • 單緩存實(shí)例的數(shù)據(jù)查詢、配置動(dòng)態(tài)變更、緩存數(shù)據(jù)失效等功能。

3.1 緩存實(shí)例的全局管控

圖片

說明:

  • 以應(yīng)用維度+機(jī)器維度展示該應(yīng)用下包含的緩存實(shí)例對象,每個(gè)實(shí)例包含緩存設(shè)置中的大小、過期策略、過期時(shí)間、內(nèi)存占用、緩存命中率等信息。
  • 單實(shí)例維度的內(nèi)存占用和緩存命中率支持以趨勢圖進(jìn)行展示。
  • 單實(shí)例維度支持配置變更操作和緩存查詢操作。

3.2 內(nèi)存占用趨勢

圖片

說明:

  • 內(nèi)存占用趨勢記錄該緩存實(shí)例對象近一段時(shí)間內(nèi)存占用的趨勢變化。
  • 時(shí)間周期目前支持展示近兩天的數(shù)據(jù)。

3.3 命中率趨勢

圖片

說明:

  • 命中率趨勢記錄該緩存實(shí)例對象近一段時(shí)間緩存命中的變化情況。
  • 時(shí)間周期目前支持展示近兩天的數(shù)據(jù)。

3.4 配置變更

圖片

說明:

  • 配置變更目前支持緩存大小和過期時(shí)間的動(dòng)態(tài)設(shè)置。
  • 目前暫時(shí)支持單實(shí)例的設(shè)置,后續(xù)會(huì)支持全量生效功能。

3.5 緩存查詢

圖片

說明:

  • 單實(shí)例維度支持緩存數(shù)據(jù)的查詢。
  • 目前支持常見的緩存Key類型包括String類型、Long類型、Int類型。

四、原理實(shí)現(xiàn)

4.1 整體設(shè)計(jì)框架

Caffeine框架功能整合

圖片

說明:

  • 沿用Caffeine的基礎(chǔ)功能包括Caffeine的緩存功能和Caffeine統(tǒng)計(jì)功能。
  • 新增Caffeine內(nèi)存占用預(yù)估功能,該功能主要是預(yù)估緩存實(shí)例對象占用的內(nèi)存情況。
  • 新增Caffeine實(shí)例命名功能,該功能是針對每個(gè)實(shí)例對象提供命名功能,是全局管控的基礎(chǔ)。
  • 新增Caffeine實(shí)例全局管控功能,該功能主要維護(hù)項(xiàng)目運(yùn)行中所有的緩存實(shí)例。

Caffeine可視化框架

圖片

說明:

  • 【項(xiàng)目工程側(cè)】:Caffeine的可視化框架基于Caffeine框架功能整合的基礎(chǔ)上增加通信層進(jìn)行數(shù)據(jù)數(shù)據(jù)上報(bào)和配置的下發(fā)。
  • 【管控平臺側(cè)】:負(fù)責(zé)緩存數(shù)據(jù)上報(bào)的接收展示,配置變更命令的下發(fā)。
  • 【通信層支持push和pull兩種模式】,push模式主要用于統(tǒng)計(jì)數(shù)據(jù)的實(shí)時(shí)上報(bào),pull模式主要用于配置下發(fā)和緩存數(shù)據(jù)查詢。

4.2 源碼實(shí)現(xiàn)

業(yè)務(wù)層-緩存對象的管理

static Cache<String, List<String>> accountWhiteCache = Caffeine.newBuilder()
            .expireAfterWrite(VivoConfigManager.getInteger("trade.account.white.list.cache.ttl", 10), TimeUnit.MINUTES)
            .recordStats().maximumSize(VivoConfigManager.getInteger("trade.account.white.list.cache.size", 100)).build();
常規(guī)的Caffeine實(shí)例的創(chuàng)建方式
 
 
static Cache<String, List<String>> accountWhiteCache = Caffeine.newBuilder().applyName("accountWhiteCache")
            .expireAfterWrite(VivoConfigManager.getInteger("trade.account.white.list.cache.ttl", 10), TimeUnit.MINUTES)
            .recordStats().maximumSize(VivoConfigManager.getInteger("trade.account.white.list.cache.size", 100)).build();
支持實(shí)例命名的Caffeine實(shí)例的創(chuàng)建方式

說明:

  • 在Caffeine實(shí)例創(chuàng)建的基礎(chǔ)上增加了緩存實(shí)例的命名功能,通過.applyName("accountWhiteCache")來定義緩存實(shí)例的命名。
public final class Caffeine<K, V> {
 
  /**
   * caffeine的實(shí)例名稱
   */
  String instanceName;
 
  /**
   * caffeine的實(shí)例維護(hù)的Map信息
   */
  static Map<String, Cache> cacheInstanceMap = new ConcurrentHashMap<>();
 
  @NonNull
  public <K1 extends K, V1 extends V> Cache<K1, V1> build() {
    requireWeightWithWeigher();
    requireNonLoadingCache();
 
    @SuppressWarnings("unchecked")
    Caffeine<K1, V1> self = (Caffeine<K1, V1>) this;
    Cache localCache =  isBounded() ? new BoundedLocalCache.BoundedLocalManualCache<>(self) : new UnboundedLocalCache.UnboundedLocalManualCache<>(self);
 
    if (null != localCache && StringUtils.isNotEmpty(localCache.getInstanceName())) {
      cacheInstanceMap.put(localCache.getInstanceName(), localCache);
    }
 
    return localCache;
  }
}

說明:

  • 每個(gè)Caffeine都有一個(gè)實(shí)例名稱instanceName。
  • 全局通過cacheInstanceMap來維護(hù)Caffeine實(shí)例對象的名稱和實(shí)例的映射關(guān)系。
  • 通過維護(hù)映射關(guān)系能夠通過實(shí)例的名稱查詢到緩存實(shí)例對象并對緩存實(shí)例對象進(jìn)行各類的操作。
  • Caffeine實(shí)例的命名功能是其他功能整合的基石。

業(yè)務(wù)層-內(nèi)存占用的預(yù)估

import jdk.nashorn.internal.ir.debug.ObjectSizeCalculator;
 
public abstract class BoundedLocalCache<K, V> extends BLCHeader.DrainStatusRef<K, V>
    implements LocalCache<K, V> {
 
  final ConcurrentHashMap<Object, Node<K, V>> data;
 
  @Override
  public long getMemoryUsed() {
    // 預(yù)估內(nèi)存占用
    return ObjectSizeCalculator.getObjectSize(data);
  }
}

說明:

  • 通過ObjectSizeCalculator.getObjectSize預(yù)估內(nèi)存的緩存值。
  • data值是Caffeine實(shí)例用來保存真實(shí)數(shù)據(jù)的對象。

業(yè)務(wù)層-數(shù)據(jù)上報(bào)機(jī)制

public static StatsData getCacheStats(String instanceName) {
 
    Cache cache = Caffeine.getCacheByInstanceName(instanceName);
 
    CacheStats cacheStats = cache.stats();
    StatsData statsData = new StatsData();
 
    statsData.setInstanceName(instanceName);
    statsData.setTimeStamp(System.currentTimeMillis()/1000);
    statsData.setMemoryUsed(String.valueOf(cache.getMemoryUsed()));
    statsData.setEstimatedSize(String.valueOf(cache.estimatedSize()));
    statsData.setRequestCount(String.valueOf(cacheStats.requestCount()));
    statsData.setHitCount(String.valueOf(cacheStats.hitCount()));
    statsData.setHitRate(String.valueOf(cacheStats.hitRate()));
    statsData.setMissCount(String.valueOf(cacheStats.missCount()));
    statsData.setMissRate(String.valueOf(cacheStats.missRate()));
    statsData.setLoadCount(String.valueOf(cacheStats.loadCount()));
    statsData.setLoadSuccessCount(String.valueOf(cacheStats.loadSuccessCount()));
    statsData.setLoadFailureCount(String.valueOf(cacheStats.loadFailureCount()));
    statsData.setLoadFailureRate(String.valueOf(cacheStats.loadFailureRate()));
 
    Optional<Eviction> optionalEviction = cache.policy().eviction();
    optionalEviction.ifPresent(eviction -> statsData.setMaximumSize(String.valueOf(eviction.getMaximum())));
 
    Optional<Expiration> optionalExpiration = cache.policy().expireAfterWrite();
    optionalExpiration.ifPresent(expiration -> statsData.setExpireAfterWrite(String.valueOf(expiration.getExpiresAfter(TimeUnit.SECONDS))));
 
    optionalExpiration = cache.policy().expireAfterAccess();
    optionalExpiration.ifPresent(expiration -> statsData.setExpireAfterAccess(String.valueOf(expiration.getExpiresAfter(TimeUnit.SECONDS))));
 
    optionalExpiration = cache.policy().refreshAfterWrite();
    optionalExpiration.ifPresent(expiration -> statsData.setRefreshAfterWrite(String.valueOf(expiration.getExpiresAfter(TimeUnit.SECONDS))));
 
    return statsData;
}

說明:

  • 通過Caffeine自帶的統(tǒng)計(jì)接口來統(tǒng)計(jì)相關(guān)數(shù)值。
  • 統(tǒng)計(jì)數(shù)據(jù)實(shí)例維度進(jìn)行統(tǒng)計(jì)。
public static void sendReportData() {
 
    try {
        if (!VivoConfigManager.getBoolean("memory.caffeine.data.report.switch", true)) {
            return;
        }
 
        // 1、獲取所有的cache實(shí)例對象
        Method listCacheInstanceMethod = HANDLER_MANAGER_CLASS.getMethod("listCacheInstance", null);
        List<String> instanceNames = (List)listCacheInstanceMethod.invoke(null, null);
        if (CollectionUtils.isEmpty(instanceNames)) {
            return;
        }
 
        String appName = System.getProperty("app.name");
        String localIp = getLocalIp();
        String localPort = String.valueOf(NetPortUtils.getWorkPort());
        ReportData reportData = new ReportData();
        InstanceData instanceData = new InstanceData();
        instanceData.setAppName(appName);
        instanceData.setIp(localIp);
        instanceData.setPort(localPort);
 
        // 2、遍歷cache實(shí)例對象獲取緩存監(jiān)控?cái)?shù)據(jù)
        Method getCacheStatsMethod = HANDLER_MANAGER_CLASS.getMethod("getCacheStats", String.class);
        Map<String, StatsData> statsDataMap = new HashMap<>();
        instanceNames.stream().forEach(instanceName -> {
 
            try {
                StatsData statsData = (StatsData)getCacheStatsMethod.invoke(null, instanceName);
 
                statsDataMap.put(instanceName, statsData);
            } catch (Exception e) {
 
            }
        });
 
        // 3、構(gòu)建上報(bào)對象
        reportData.setInstanceData(instanceData);
        reportData.setStatsDataMap(statsDataMap);
 
        // 4、發(fā)送Http的POST請求
        HttpPost httpPost = new HttpPost(getReportDataUrl());
        httpPost.setConfig(requestConfig);
 
        StringEntity stringEntity = new StringEntity(JSON.toJSONString(reportData));
        stringEntity.setContentType("application/json");
        httpPost.setEntity(stringEntity);
 
        HttpResponse response = httpClient.execute(httpPost);
        String result = EntityUtils.toString(response.getEntity(),"UTF-8");
        EntityUtils.consume(response.getEntity());
 
        logger.info("Caffeine 數(shù)據(jù)上報(bào)成功 URL {} 參數(shù) {} 結(jié)果 {}", getReportDataUrl(), JSON.toJSONString(reportData), result);
    } catch (Throwable throwable) {
        logger.error("Caffeine 數(shù)據(jù)上報(bào)失敗 URL {} ", getReportDataUrl(), throwable);
    }
}

說明:

  • 通過獲取項(xiàng)目中運(yùn)行的所有Caffeine實(shí)例并依次遍歷收集統(tǒng)計(jì)數(shù)據(jù)。
  • 通過http協(xié)議負(fù)責(zé)上報(bào)對應(yīng)的統(tǒng)計(jì)數(shù)據(jù),采用固定間隔周期進(jìn)行上報(bào)。

業(yè)務(wù)層-配置動(dòng)態(tài)下發(fā)

public static ExecutionResponse dispose(ExecutionRequest request) {
    ExecutionResponse executionResponse = new ExecutionResponse();
    executionResponse.setCmdType(CmdTypeEnum.INSTANCE_CONFIGURE.getCmd());
    executionResponse.setInstanceName(request.getInstanceName());
 
    String instanceName = request.getInstanceName();
    Cache cache = Caffeine.getCacheByInstanceName(instanceName);
 
    // 設(shè)置緩存的最大條目
    if (null != request.getMaximumSize() && request.getMaximumSize() > 0) {
        Optional<Eviction> optionalEviction = cache.policy().eviction();
        optionalEviction.ifPresent(eviction ->eviction.setMaximum(request.getMaximumSize()));
    }
 
    // 設(shè)置寫后過期的過期時(shí)間
    if (null != request.getExpireAfterWrite() && request.getExpireAfterWrite() > 0) {
        Optional<Expiration> optionalExpiration = cache.policy().expireAfterWrite();
        optionalExpiration.ifPresent(expiration -> expiration.setExpiresAfter(request.getExpireAfterWrite(), TimeUnit.SECONDS));
    }
 
    // 設(shè)置訪問過期的過期時(shí)間
    if (null != request.getExpireAfterAccess() && request.getExpireAfterAccess() > 0) {
        Optional<Expiration> optionalExpiration = cache.policy().expireAfterAccess();
        optionalExpiration.ifPresent(expiration -> expiration.setExpiresAfter(request.getExpireAfterAccess(), TimeUnit.SECONDS));
    }
 
    // 設(shè)置寫更新的過期時(shí)間
    if (null != request.getRefreshAfterWrite() && request.getRefreshAfterWrite() > 0) {
 
        Optional<Expiration> optionalExpiration = cache.policy().refreshAfterWrite();
        optionalExpiration.ifPresent(expiration -> expiration.setExpiresAfter(request.getRefreshAfterWrite(), TimeUnit.SECONDS));
    }
 
    executionResponse.setCode(0);
    executionResponse.setMsg("success");
 
    return executionResponse;
}

說明:

  • 通過Caffeine自帶接口進(jìn)行緩存配置的相關(guān)設(shè)置。

業(yè)務(wù)層-緩存數(shù)據(jù)清空

/**
     * 失效緩存的值
     * @param request
     * @return
     */
    public static ExecutionResponse invalidate(ExecutionRequest request) {
 
        ExecutionResponse executionResponse = new ExecutionResponse();
        executionResponse.setCmdType(CmdTypeEnum.INSTANCE_INVALIDATE.getCmd());
        executionResponse.setInstanceName(request.getInstanceName());
 
        try {
            // 查找對應(yīng)的cache實(shí)例
            String instanceName = request.getInstanceName();
            Cache cache = Caffeine.getCacheByInstanceName(instanceName);
 
            // 處理清空指定實(shí)例的所有緩存 或 指定實(shí)例的key對應(yīng)的緩存
            Object cacheKeyObj = request.getCacheKey();
 
            // 清除所有緩存
            if (Objects.isNull(cacheKeyObj)) {
                cache.invalidateAll();
            } else {
                // 清除指定key對應(yīng)的緩存
                if (Objects.equals(request.getCacheKeyType(), 2)) {
                    cache.invalidate(Long.valueOf(request.getCacheKey().toString()));
                } else if (Objects.equals(request.getCacheKeyType(), 3)) {
                    cache.invalidate(Integer.valueOf(request.getCacheKey().toString()));
                } else {
                    cache.invalidate(request.getCacheKey().toString());
                }
            }
 
            executionResponse.setCode(0);
            executionResponse.setMsg("success");
        } catch (Exception e) {
            executionResponse.setCode(-1);
            executionResponse.setMsg("fail");
        }
 
        return executionResponse;
    }
}

業(yè)務(wù)層-緩存數(shù)據(jù)查詢

public static ExecutionResponse inspect(ExecutionRequest request) {
 
    ExecutionResponse executionResponse = new ExecutionResponse();
    executionResponse.setCmdType(CmdTypeEnum.INSTANCE_INSPECT.getCmd());
    executionResponse.setInstanceName(request.getInstanceName());
 
    String instanceName = request.getInstanceName();
    Cache cache = Caffeine.getCacheByInstanceName(instanceName);
 
    Object cacheValue = cache.getIfPresent(request.getCacheKey());
    if (Objects.equals(request.getCacheKeyType(), 2)) {
        cacheValue = cache.getIfPresent(Long.valueOf(request.getCacheKey().toString()));
    } else if (Objects.equals(request.getCacheKeyType(), 3)) {
        cacheValue = cache.getIfPresent(Integer.valueOf(request.getCacheKey().toString()));
    } else {
        cacheValue = cache.getIfPresent(request.getCacheKey().toString());
    }
 
    if (Objects.isNull(cacheValue)) {
        executionResponse.setData("");
    } else {
        executionResponse.setData(JSON.toJSONString(cacheValue));
    }
 
    return executionResponse;
}

說明:

  • 通過Caffeine自帶接口進(jìn)行緩存信息查詢。

通信層-監(jiān)聽服務(wù)

public class ServerManager {
 
    private Server jetty;
 
    /**
     * 創(chuàng)建jetty對象
     * @throws Exception
     */
    public ServerManager() throws Exception {
 
        int port = NetPortUtils.getAvailablePort();
 
        jetty = new Server(port);
 
        ServletContextHandler context = new ServletContextHandler(ServletContextHandler.NO_SESSIONS);
        context.setContextPath("/");
        context.addServlet(ClientServlet.class, "/caffeine");
        jetty.setHandler(context);
    }
 
    /**
     * 啟動(dòng)jetty對象
     * @throws Exception
     */
    public void start() throws Exception {
        jetty.start();
    }
}
 
 
public class ClientServlet extends HttpServlet {
 
    private static final Logger logger = LoggerFactory.getLogger(ClientServlet.class);
 
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        super.doGet(req, resp);
    }
 
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
 
        ExecutionResponse executionResponse = null;
        String requestJson = null;
        try {
            // 獲取請求的相關(guān)的參數(shù)
            String contextPath = req.getContextPath();
            String servletPath = req.getServletPath();
            String requestUri = req.getRequestURI();
            requestJson = IOUtils.toString(req.getInputStream(), StandardCharsets.UTF_8);
 
            // 處理不同的命令
            ExecutionRequest executionRequest = JSON.parseObject(requestJson, ExecutionRequest.class);
 
            // 通過反射來來處理類依賴問題
            executionResponse = DisposeCenter.dispatch(executionRequest);
 
        } catch (Exception e) {
            logger.error("vivo-memory 處理請求異常 {} ", requestJson, e);
        }
 
        if (null == executionResponse) {
            executionResponse = new ExecutionResponse();
            executionResponse.setCode(-1);
            executionResponse.setMsg("處理異常");
        }
 
        // 組裝相應(yīng)報(bào)文
        resp.setContentType("application/json; charset=utf-8");
        PrintWriter out = resp.getWriter();
        out.println(JSON.toJSONString(executionResponse));
        out.flush();
    }
}

說明:

  • 通信層通過jetty啟動(dòng)http服務(wù)進(jìn)行監(jiān)聽,安全考慮端口不對外開放。
  • 通過定義ClientServlet來處理相關(guān)的請求包括配置下發(fā)和緩存查詢等功能。

通信層-心跳設(shè)計(jì)

/**
 * 發(fā)送心跳數(shù)據(jù)
 */
public static void sendHeartBeatData() {
 
    try {
 
        if (!VivoConfigManager.getBoolean("memory.caffeine.heart.report.switch", true)) {
            return;
        }
 
        // 1、構(gòu)建心跳數(shù)據(jù)
        String appName = System.getProperty("app.name");
        String localIp = getLocalIp();
        String localPort = String.valueOf(NetPortUtils.getWorkPort());
 
        HeartBeatData heartBeatData = new HeartBeatData();
        heartBeatData.setAppName(appName);
        heartBeatData.setIp(localIp);
        heartBeatData.setPort(localPort);
        heartBeatData.setTimeStamp(System.currentTimeMillis()/1000);
 
        // 2、發(fā)送Http的POST請求
        HttpPost httpPost = new HttpPost(getHeartBeatUrl());
        httpPost.setConfig(requestConfig);
 
        StringEntity stringEntity = new StringEntity(JSON.toJSONString(heartBeatData));
        stringEntity.setContentType("application/json");
        httpPost.setEntity(stringEntity);
 
        HttpResponse response = httpClient.execute(httpPost);
        String result = EntityUtils.toString(response.getEntity(),"UTF-8");
        EntityUtils.consume(response.getEntity());
 
        logger.info("Caffeine 心跳上報(bào)成功 URL {} 參數(shù) {} 結(jié)果 {}", getHeartBeatUrl(), JSON.toJSONString(heartBeatData), result);
    } catch (Throwable throwable) {
        logger.error("Caffeine 心跳上報(bào)失敗 URL {} ", getHeartBeatUrl(), throwable);
    }
}

說明:

  • 心跳功能上報(bào)項(xiàng)目實(shí)例的ip和端口用來通信,攜帶時(shí)間戳用來記錄上報(bào)時(shí)間戳。
  • 實(shí)際項(xiàng)目中因?yàn)闄C(jī)器的回收等場景需要通過上報(bào)時(shí)間戳定時(shí)清理下線的服務(wù)。

五、總結(jié)

vivo技術(shù)團(tuán)隊(duì)在Caffeine的使用經(jīng)驗(yàn)上曾有過多次分享,可參考公眾號文章《如何把 Caffeine Cache 用得如絲般順滑》,此篇文章在使用的基礎(chǔ)上基于使用痛點(diǎn)進(jìn)行進(jìn)一步的定制。

目前Caffeine可視化的項(xiàng)目已經(jīng)在相關(guān)核心業(yè)務(wù)場景中落地并發(fā)揮作用,整體運(yùn)行平穩(wěn)。使用較多的功能包括項(xiàng)目維度的caffeine實(shí)例的全局管控,單實(shí)例維度的內(nèi)存占用評估和緩存命中趨勢評估。

如通過單實(shí)例的內(nèi)存占用評估功能能夠合理評估緩存條目設(shè)置和內(nèi)存占用之間的關(guān)系;通過分析緩存命中率的整體趨勢評估緩存的參數(shù)設(shè)置合理性。

期待此篇文章能夠給業(yè)界緩存使用和監(jiān)控帶來一些新思路。

責(zé)任編輯:龐桂玉 來源: vivo互聯(lián)網(wǎng)技術(shù)
相關(guān)推薦

2023-11-30 09:34:14

數(shù)據(jù)可視化探索

2024-03-06 19:57:56

探索商家可視化

2022-04-15 10:30:03

美團(tuán)技術(shù)實(shí)踐

2023-11-13 11:27:58

攜程可視化

2020-10-22 08:52:52

Python數(shù)據(jù)集可視化

2015-10-14 17:59:53

Google數(shù)據(jù)探索交互開發(fā)

2017-10-25 13:04:10

數(shù)據(jù)可視化信息可視化數(shù)據(jù)圖表

2020-03-11 14:39:26

數(shù)據(jù)可視化地圖可視化地理信息

2023-02-07 07:03:39

2020-07-22 10:30:54

數(shù)據(jù)可視化分析平臺分析工具

2023-05-06 12:57:34

Python工具

2017-05-18 11:43:41

Android模塊化軟件

2020-03-07 21:48:46

物聯(lián)網(wǎng)可視化技術(shù)設(shè)計(jì)

2017-07-12 16:07:49

大數(shù)據(jù)數(shù)據(jù)可視化

2022-05-12 16:27:07

數(shù)字化運(yùn)營可視化管理數(shù)字化

2022-08-26 09:15:58

Python可視化plotly

2024-10-16 13:47:40

2021-06-24 11:24:32

安全掛圖作戰(zhàn)

2021-02-25 21:47:47

開源技術(shù) 趨勢

2009-04-21 14:26:41

可視化監(jiān)控IT管理摩卡
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號