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

緩存監(jiān)控治理在游戲業(yè)務(wù)的實(shí)踐和探索

數(shù)據(jù)庫
通過對 Redis 和 Caffeine 的緩存監(jiān)控快速發(fā)現(xiàn)和定位問題降低故障的影響面。

一、緩存監(jiān)控的背景

  • 游戲業(yè)務(wù)中存在大量的高頻請求尤其是對熱門游戲而言,而應(yīng)對高并發(fā)場景緩存是一個常見且有效的手段。
  • 游戲業(yè)務(wù)中大量的采用遠(yuǎn)程緩存(Redis)和本地緩存(Caffeine)組合方式來應(yīng)對大流量的場景。
  • 在整個緩存使用的實(shí)踐過程中,基于真實(shí)線上案例和日常緩存運(yùn)維痛點(diǎn)沉淀了一些緩存監(jiān)控治理的有效案例供分享。

二、遠(yuǎn)程緩存的監(jiān)控介紹

2.1 監(jiān)控的方案

2.1.1 監(jiān)控目的

  • 從宏觀來講監(jiān)控本質(zhì)目的是為了及時發(fā)現(xiàn)定位并解決問題,在成本可控的前提下監(jiān)控維度盡可能豐富。
  • 聚焦到 Redis 的維度,除了 Server 本身的監(jiān)控指標(biāo)(如請求量、連接數(shù)外),還需要監(jiān)控更多偏業(yè)務(wù)的指標(biāo)。
  • Redis 目前最常見的問題包括:熱點(diǎn) Key 問題,大 Key 問題,超負(fù)載的大請求量問題。
  • 聚焦上述的問題,在基于 Redis 的原生監(jiān)控指標(biāo)基礎(chǔ)上,補(bǔ)充更多的包含業(yè)務(wù)屬性的監(jiān)控。

2.1.2 監(jiān)控方案

  • 目前從監(jiān)控的維度進(jìn)行分析,希望能做到既能針對某個 key 的熱點(diǎn)監(jiān)控,又能針對某一類相同前綴的 key 做聚合趨勢監(jiān)控。前者目的是發(fā)現(xiàn)熱點(diǎn) key,后者目的是從趨勢維度監(jiān)控緩存的實(shí)際訪問量。
  • Redis 的具體 key的監(jiān)控由 Redis 自研團(tuán)隊(duì)集成到 redis server 側(cè)實(shí)現(xiàn)監(jiān)控,分類上歸屬為 Redis Server 側(cè)的監(jiān)控,這部分不在本篇分享中具體展開。
  • Redis 的某類相同前綴的 key 的聚合監(jiān)控由業(yè)務(wù)側(cè)通過Aspect 攔截器攔截并上報(bào)埋點(diǎn)實(shí)現(xiàn),其中 key 的設(shè)計(jì)需要遵循便于聚合的原則,分類上歸屬為 Redis 的業(yè)務(wù)側(cè)的監(jiān)控。

2.1.3 監(jiān)控大盤

圖片

【Redis Server系統(tǒng)監(jiān)控指標(biāo)】

說明:

  • 上圖監(jiān)控 Redis Server 的原生指標(biāo),具體可以參考 Redis 官方文檔 
    http://doc.redisfans.com/server/info.html。
  • 上述指標(biāo)用來評估 Redis Server 本身的負(fù)載情況,并基于此考慮是否需要橫向和縱向擴(kuò)容。

圖片

【Redis 業(yè)務(wù)維度前綴監(jiān)控指標(biāo)】

說明:

  • 上圖監(jiān)控的業(yè)務(wù)維度按照某類 key 的前綴進(jìn)行聚合的指標(biāo),評估各類的 Redis 的 key 的讀寫指標(biāo)。
  • 上述指標(biāo)用來評估業(yè)務(wù)對 Redis 緩存使用的合理性,如發(fā)現(xiàn)某個前綴 key 的寫入量太大(緩存應(yīng)該是讀多寫少場景)就需要思考緩存設(shè)計(jì)的合理性。

2.2 監(jiān)控的實(shí)現(xiàn)

  • 業(yè)務(wù)維度按照某類 key 的前綴進(jìn)行聚合的功能,關(guān)鍵的實(shí)現(xiàn)邏輯包括:一類業(yè)務(wù)需要統(tǒng)一前綴 key 并在末尾拼接變量;通過切面攔截 redis 的讀寫并上報(bào)埋點(diǎn)。
  • 統(tǒng)一前綴 key 是指:如果業(yè)務(wù)A是按照用戶維度進(jìn)行緩存 Key 的設(shè)計(jì),那么 Key 的形態(tài)應(yīng)該是 Prefix:UserId,Prefix 是業(yè)務(wù)場景的前綴,UserId 是用戶維度的動態(tài)值。
  • 切面攔截是指:針對指定的Redis操作(包括常見的 Set 等),進(jìn)行攔截并匹配前綴進(jìn)行埋點(diǎn)上報(bào)。

2.2.1 前綴 key 設(shè)計(jì)

Redis Key 的設(shè)計(jì)

public class RedisKeyConstants {


    public static final String    REDIS_GAMEGROUP_NEW_KEY              = "newgamegroup";
    public static final String    REDIS_GAMEGROUP_DETAIL_KEY          = "gamegroup:detail";
    public static final String    REDIS_KEY_IUNIT_STRATEGY_COUNT      = "activity:ihandler:strategy:count";
    public static final String    CONTENT_DISTRIBUTE_CURRENT          = "content:distribute:current";
    public static final String    RECOMMEND_NOTE                      = "recommend:note";
}




public class RedisUtils {


    public static final String    COMMON_REDIS_KEY_SPLIT    = ":";


    public static String buildRedisKey(String key, Object... params) {
        if (params == null || params.length == 0) {
            return key;
        }


        for (Object param : params) {
            key += COMMON_REDIS_KEY_SPLIT + param;
        }


        return key;
    }
}

左右滑動查看完整代碼

說明:

  • 在常量定義 RedisKeyConstants 中按照不同的業(yè)務(wù)區(qū)分了不同的業(yè)務(wù)場景的前綴 Key。
  • 在 RedisUtils#buildRedisKey 中將業(yè)務(wù)的前綴和動態(tài)變化的參數(shù)進(jìn)行拼接,中間通過分隔符進(jìn)行連接。
  • 分割符的引入是為了后續(xù)切面攔截時候進(jìn)行逆向切割獲取前綴使用。

2.2.2 監(jiān)控實(shí)現(xiàn)

@Slf4j
@Aspect
@Order(0)
@Component
public class RedisMonitorAspect {
    private static final String PREFIX_CONFIG = "redis.monitor.prefix";
    private static final Set<String> PREFIX_SET = new HashSet<>();
    @Resource
    private MonitorComponent monitorComponent;
    static {
        // 更新前綴匹配的名單
        String prefixValue = VivoConfigManager.getString(PREFIX_CONFIG, "");
        refreshConf(prefixValue);
        // 增加配置變更的回調(diào)
        VivoConfigManager.addListener(new VivoConfigListener() {
            @Override
            public void eventReceived(PropertyItem propertyItem, ChangeEventType changeEventType) {
                if (StringUtils.equalsIgnoreCase(propertyItem.getName(), PREFIX_CONFIG)) {
                    refreshConf(propertyItem.getValue());
                }
            }
        });
    }
    /**
     * 更新前綴匹配的名單
     * @param prefixValue
     */
    private static void refreshConf(String prefixValue) {
        if (StringUtils.isNotEmpty(prefixValue)) {
            String[] prefixArr = StringUtils.split(prefixValue, ",");
            Arrays.stream(prefixArr).forEach(item -> PREFIX_SET.add(item));
        }
    }
    @Pointcut("execution(* com.vivo.joint.dal.common.redis.dao.RedisDao.set*(..))")
    public void point() {
    }
    @Around("point()")
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        //業(yè)務(wù)邏輯異常情況直接拋到業(yè)務(wù)層處理
        Object result = pjp.proceed();
        try {
            if (VivoConfigManager.getBoolean("joint.center.redis.monitor.switch", true)) {
                Object[] args = pjp.getArgs();
                if (null != args && args.length > 0) {
                    String redisKey = String.valueOf(args[0]);
                    if (VivoConfigManager.getBoolean("joint.center.redis.monitor.send.log.switch", true)) {
                        LOGGER.info("更新redis的緩存 {}", redisKey);
                    }
                    String monitorKey = null;
                    // 先指定前綴匹配
                    if (!PREFIX_SET.isEmpty()) {
                        for (String prefix : PREFIX_SET) {
                            if (StringUtils.startsWithIgnoreCase(redisKey, prefix)) {
                                monitorKey = prefix;
                                break;
                            }
                        }
                    }
                    if (StringUtils.isEmpty(monitorKey) && StringUtils.contains(redisKey, ":")) {
                        // 需要考慮前綴的格式,保證數(shù)據(jù)寫入不能膨脹
                        monitorKey = StringUtils.substringBeforeLast(redisKey, ":");
                    }
                    monitorComponent.sendRedisMonitorData(monitorKey);
                }
            }
        } catch (Exception e) {
        }
        return result;
    }
}
printf("hello world!");

說明:

  • 通過 Aspect 的切面功能對 Redis 的指定操作進(jìn)行攔截,如上圖中的 Set 操作等,可以按需擴(kuò)展到其他操作(包括 get 命令等)。
  • 針對前綴 key 的提取支持兩個維度,默認(rèn)場景和自定義場景,其中處理優(yōu)先級為 自定義場景 > 默認(rèn)場景
  • 默認(rèn)場景是指如 Redis 的 Key 為 A:B:C:UserId,從后往前尋找后向第一個分割符進(jìn)行分割,A:B:C:UserId 分割后的根據(jù)前綴 A:B:C 進(jìn)行聚合后數(shù)據(jù)埋點(diǎn)上報(bào)。
  • 自定義場景如 Redis的 Key 為 A:B:UserId,通過配置自定義的前綴 A:B 來匹配,A:B:C:UserId 根據(jù)自定義的前綴分割后根據(jù)前綴 A:B 進(jìn)行聚合后數(shù)據(jù)埋點(diǎn)上報(bào)。
  • 考慮自定義場景的靈活性,相關(guān)的自定義前綴通過配置中心實(shí)時生效。

2.3 監(jiān)控的案例

public static final String REDISKEY_USER_POPUP_PLAN = "popup:user:plan";
  
    public PopupWindowPlan findPlan(FindPlanParam param) {
        String openId = param.getOpenId();
        String imei = param.getImei();
        String gamePackage = param.getGamePackage();
        Integer planType = param.getPlanType();
        String appId = param.getAppId();




        // 1、獲取緩存的數(shù)據(jù)
        PopupWindowPlan cachedPlan = getPlanFromCache(openId, imei, gamePackage, planType);
        if (cachedPlan != null) {
            monitorPopWinPlan(cachedPlan);
        
            return cachedPlan;
        }


        // 2、未命中換成后從持久化部分獲取對應(yīng)的 PopupWindowPlan 對象


        // 3、保存到Redis換成
      setPlanToCache(openId, imei, gamePackage, plan);


        return cachedPlan;
    }


    // 從緩存中獲取數(shù)據(jù)的邏輯
    private PopupWindowPlan getPlanFromCache(String openId, String imei, String gamePackage, Integer planType) {


        String key = RedisUtils.buildRedisKey(RedisKeyConstants.REDISKEY_USER_POPUP_PLAN, openId, imei, gamePackage, planType);
        String cacheValue = redisDao.get(key);
        if (StringUtils.isEmpty(cacheValue)) {
            return null;
        }


        try {
            PopupWindowPlan plan = objectMapper.readValue(cacheValue, PopupWindowPlan.class);
            return plan;
        } catch (Exception e) {
        }


        return null;
    }


    // 保存數(shù)據(jù)到緩存當(dāng)中
    private void setPlanToCache(String openId, String imei, String gamePackage, PopupWindowPlan plan, Integer planType) {


        String key = RedisUtils.buildRedisKey(RedisKeyConstants.REDISKEY_USER_POPUP_PLAN, openId, imei, gamePackage, planType);
        try {
            String serializedStr = objectMapper.writeValueAsString(plan);
            redisDao.set(key, serializedStr, VivoConfigManager.getInteger(ConfigConstants.POPUP_PLAN_CACHE_EXPIRE_TIME, 300));
        } catch (Exception e) {
        }
    }

左右滑動查看完整代碼

說明:

  • 如監(jiān)控實(shí)現(xiàn)部分所述,通過 Redis Key 的前綴聚合監(jiān)控,能夠發(fā)現(xiàn)某一類業(yè)務(wù)場景的 Redis 的寫請求數(shù),進(jìn)而發(fā)現(xiàn) Redis 的無效使用場景。
  • 上述案例是典型的Redis的緩存使用場景:1.訪問 Redis 緩存;2.若命中則直接返回結(jié)果;3、如未命中則查詢持久化存儲獲取數(shù)據(jù)并寫入 Redis 緩存。
  • 從業(yè)務(wù)監(jiān)控的大盤發(fā)現(xiàn)前綴 popup:user:plan 存在大量的 set 操作命令,按照緩存讀多寫少的原則,該場景標(biāo)明該緩存的設(shè)計(jì)是無效的。
  • 通過業(yè)務(wù)分析后,發(fā)現(xiàn)在游戲的業(yè)務(wù)場景中 用戶維度+游戲維度 不存在5分鐘重復(fù)訪問緩存的場景,確認(rèn)緩存的無效。
  • 確認(rèn)緩存無效后,刪除相關(guān)的緩存邏輯,降低了 Redis Server 的負(fù)載后并進(jìn)一步提升了接口的響應(yīng)時間。

三、本地緩存的監(jiān)控介紹

3.1 監(jiān)控的方案

3.1.1 監(jiān)控目的

  • 從宏觀來講監(jiān)控本質(zhì)目的是為了及時發(fā)現(xiàn)定位并解決問題,在成本可控的前提下監(jiān)控維度盡可能豐富。
  • 聚焦到 Caffeine 的維度,監(jiān)控指標(biāo)包括緩存的請求次數(shù)、命中率,未命中率等指標(biāo)。
  • Caffeine 目前最常見的問題是:緩存設(shè)置不合理導(dǎo)致緩存穿透引發(fā)的系統(tǒng)問題。

3.1.2 監(jiān)控方案

  • 目前從監(jiān)控的維度進(jìn)行分析,按照機(jī)器維度+緩存實(shí)例進(jìn)行監(jiān)控指標(biāo)采集,其中監(jiān)控指標(biāo)的采集基于 Caffeine 的 recordStats 功能開啟。
  • 基于 caffeine 的原生能力定制的 vivo-caffeine 集成了單機(jī)器維度+單緩存實(shí)例的指標(biāo)數(shù)據(jù)的采集和上報(bào)功能。
  • vivo-caffeine 上報(bào)的數(shù)據(jù)會按照單機(jī)器+單緩存實(shí)例維度進(jìn)行大盤展示,支持全量指標(biāo)的查詢功能。
  • vivo-caffeine 的上報(bào)的數(shù)據(jù)和公司級的告警功能相結(jié)合,例如針對緩存未命中率進(jìn)行監(jiān)控就能很快發(fā)現(xiàn)緩存穿透的問題。

3.1.3 監(jiān)控大盤

圖片

圖片

【Caffeine 系統(tǒng)監(jiān)控指標(biāo)】

說明:

  • vivo-caffeine 按照單機(jī)器 + 緩存實(shí)例維度進(jìn)行監(jiān)控?cái)?shù)據(jù)的上報(bào)并進(jìn)行展示。
  • 所有的系統(tǒng)指標(biāo)都支持查詢并以圖片的形式進(jìn)行展示。

3.2 監(jiān)控的實(shí)現(xiàn)

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;
  }
}




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();

左右滑動查看完整代碼

說明:

  • Caffeine 的按緩存實(shí)例進(jìn)行指標(biāo)采集的前提是需要全局維護(hù)緩存實(shí)例和對應(yīng)的 instanceName 之間的關(guān)聯(lián)關(guān)系。
  • Caffeine 在緩存創(chuàng)建的時候會設(shè)置實(shí)例的名稱,通過 applyName 方法設(shè)置實(shí)例名稱。
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;
}

左右滑動查看完整代碼

說明:

  • 監(jiān)控指標(biāo)的采集基于 Caffeine 原生的統(tǒng)計(jì)功能 CacheStats。
  • 所有采集的指標(biāo)封裝成一個統(tǒng)計(jì)對象 StatsData 進(jìn)行上報(bào)。
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);
    }
}

左右滑動查看完整代碼

說明:

  • 每個應(yīng)用單獨(dú)的部署的服務(wù)作為一個采集點(diǎn),進(jìn)行指標(biāo)的采集和上報(bào)。
  • 采集過程是獲取當(dāng)前部署的應(yīng)用下的所有緩存實(shí)例并進(jìn)行指標(biāo)的采集封裝。
  • 整體上報(bào)采用 Http 協(xié)議進(jìn)行上報(bào)并最終展示到監(jiān)控平臺。

3.3 監(jiān)控的案例

圖片

說明:

  • 某次線上問題發(fā)生時發(fā)現(xiàn)突然多出了大量的 Redis 的請求,但是無法具體定位請求的 Redis 的前綴 key,設(shè)想如果接入了 Redis 的業(yè)務(wù)監(jiān)控,問題來源就能很快定位。
  • 在后續(xù)的問題排查中發(fā)現(xiàn)某個 Caffeine 的本地緩存因?yàn)榇笮≡O(shè)置過小導(dǎo)致大量的本地請求緩存穿透導(dǎo)致 Redis 的請求量突增,最終導(dǎo)致 Redis 的服務(wù)接近崩潰。
  • 針對本地緩存穿透的場景,如果采用 Caffeine 的本地緩存監(jiān)控方案,能夠從緩存的命中率指標(biāo)和緩存的未命中率指標(biāo)突增突降中發(fā)現(xiàn)問題根源。

四、結(jié)束語

  • 本篇內(nèi)容是基于線上真實(shí)案例分享游戲業(yè)務(wù)側(cè)在緩存監(jiān)控治理方面的有效實(shí)踐,監(jiān)控治理本身是一個未雨綢繆的過程。在沒有線上問題發(fā)生時看似不重要,但一旦發(fā)生無法快速定位問題又會導(dǎo)致問題的放大,因此完善的緩存監(jiān)控整理其實(shí)是非常有必要的。
  • Redis 的前綴 key 的監(jiān)控思路是游戲業(yè)務(wù)服務(wù)端在優(yōu)化Redis 的使用效率的過程中發(fā)現(xiàn)的一個較好的實(shí)踐,逐步延伸后發(fā)現(xiàn)這是一個很好的監(jiān)控手段,能夠通過突增的趨勢快速定位問題。
  • 基于 Caffeine 的原生能力定制的監(jiān)控指標(biāo)采集是游戲業(yè)務(wù)服務(wù)端在探索 Caffeine 可視化過程中進(jìn)行的一個探索落地,將整個緩存實(shí)例的運(yùn)行態(tài)進(jìn)行完整呈現(xiàn),為業(yè)務(wù)穩(wěn)定性貢獻(xiàn)力量。
  • 相信業(yè)內(nèi)同仁會有更多更好的實(shí)踐,相互分享共同進(jìn)步,共勉。
責(zé)任編輯:龐桂玉 來源: vivo互聯(lián)網(wǎng)技術(shù)
相關(guān)推薦

2015-09-23 10:25:41

Docker英雄聯(lián)盟Docker實(shí)踐

2023-03-15 18:34:26

資源治理數(shù)據(jù)治理業(yè)務(wù)線

2024-10-15 08:14:51

2023-10-27 12:16:23

游戲發(fā)行平臺SOP

2016-01-12 11:38:19

智能化運(yùn)維運(yùn)維業(yè)務(wù)

2021-12-08 10:35:04

開源監(jiān)控Zabbix

2015-08-17 09:39:33

智能運(yùn)維百度監(jiān)控

2022-05-13 11:24:09

數(shù)據(jù)美團(tuán)

2019-04-30 09:00:33

SQL數(shù)據(jù)庫Apache Flin

2023-12-21 14:02:11

AIGC趣丸科技素材

2022-12-26 16:34:51

開源云原生

2024-07-25 14:04:16

2020-08-20 07:54:58

Node多線程解密

2021-08-09 10:21:42

云原生Dubbo3.0 服務(wù)治理

2023-06-23 15:17:32

數(shù)據(jù)分析

2019-04-09 15:02:36

OpenResty騰訊游戲營銷技術(shù)

2023-02-07 07:03:39

2024-10-16 20:31:25

2024-04-17 07:21:52

物化視圖查詢加速器數(shù)據(jù)倉庫

2023-05-31 14:54:32

點(diǎn)贊
收藏

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