0
  • 聊天消息
  • 系統(tǒng)消息
  • 評(píng)論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會(huì)員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識(shí)你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

得物熱點(diǎn)探測技術(shù)架構(gòu)設(shè)計(jì)與實(shí)踐分析

OSC開源社區(qū) ? 來源:得物技術(shù) ? 2023-03-06 09:08 ? 次閱讀

1

概述

說到熱點(diǎn)問題,首先我們先理解一下什么是熱點(diǎn)?

熱點(diǎn)通常意義來說,是指在一段時(shí)間內(nèi),被廣泛關(guān)注的物品或事件,例如微博熱搜,熱賣商品,熱點(diǎn)新聞,明星直播等等,所以熱點(diǎn)產(chǎn)生主要包含2個(gè)條件:1.有限時(shí)間, 2流量高聚。

7ede4e66-bb89-11ed-bfe3-dac502259ad0.png

而在互聯(lián)網(wǎng)領(lǐng)域,熱點(diǎn)又主要分為2大類:

1. 有預(yù)期的熱點(diǎn):比如在電商活動(dòng)當(dāng)中推出的爆款聯(lián)名限量款的商品,又或者是秒殺的會(huì)場活動(dòng)等

2. 無預(yù)期的熱點(diǎn):比如受到了黑客的惡意攻擊,網(wǎng)絡(luò)爬蟲頻繁訪問,又或者突發(fā)新聞帶來的流量沖擊等

針對(duì)于有預(yù)期的熱點(diǎn)可以通過熱點(diǎn)數(shù)據(jù)預(yù)熱, 流量限制和異步隊(duì)列進(jìn)行處理。但是對(duì)于突發(fā)性無感知的熱點(diǎn)數(shù)據(jù)流量,往往由于請求過于集中,導(dǎo)致訪問數(shù)據(jù)流量超出的server的正常負(fù)載水位,從而出現(xiàn)服務(wù)過載不可用的情況,這種問題被稱之為熱點(diǎn)問題。

2

熱點(diǎn)場景

看完關(guān)于熱點(diǎn)問題的簡單介紹,我們已經(jīng)理解了熱點(diǎn)產(chǎn)生的條件是短時(shí)間內(nèi)被頻繁訪問導(dǎo)致流量高聚,而流量高聚就會(huì)出現(xiàn)一系列的熱點(diǎn)問題。那被頻繁訪問的Key,就是我們通常所說的熱Key。

接下來我們來看一下哪些場景會(huì)導(dǎo)致熱點(diǎn)問題以及對(duì)應(yīng)的熱Key:

MySQL中被頻繁訪問的數(shù)據(jù) ,如熱門商品的主鍵Id

Redis緩存中被密集訪問的Key,如熱門商品的詳情需要get goods$Id

惡意攻擊或機(jī)器人爬蟲的請求信息,如特定標(biāo)識(shí)的userId、機(jī)器IP

頻繁被訪問的接口地址,如獲取用戶信息接口 /userInfo/ + userId

3

熱點(diǎn)探測技術(shù)原理

了解完什么是熱點(diǎn)問題和熱Key出現(xiàn)的場景以后,我們會(huì)提出一個(gè)疑問,如何去提前感知這些熱點(diǎn)數(shù)據(jù)?這里就需要聊到熱點(diǎn)探測技術(shù)。

3.1 熱點(diǎn)探測可以帶來什么好處?

3.1.1 提升性能

解決熱點(diǎn)問題通常會(huì)使用分布式緩存,但是在讀取時(shí)還是需要進(jìn)行網(wǎng)絡(luò)通訊,就會(huì)有額外的時(shí)間開銷。那如果能對(duì)熱點(diǎn)數(shù)據(jù)提前進(jìn)行本地緩存,即本地預(yù)熱,就能大幅提升機(jī)器讀取數(shù)據(jù)的性能,減輕下層緩存集群的壓力。

注意,本地緩存與實(shí)時(shí)數(shù)據(jù)存在不一致的風(fēng)險(xiǎn)。需要根據(jù)具體業(yè)務(wù)場景進(jìn)行評(píng)估,緩存級(jí)數(shù)越多,數(shù)據(jù)不一致的風(fēng)險(xiǎn)就越大!

3.1.2 規(guī)避風(fēng)險(xiǎn)

對(duì)于無預(yù)期的熱數(shù)據(jù)(即突發(fā)場景下形成的熱Key),可能會(huì)對(duì)業(yè)務(wù)系統(tǒng)帶來極大的風(fēng)險(xiǎn),可將風(fēng)險(xiǎn)分為兩個(gè)層次:

對(duì)數(shù)據(jù)層的風(fēng)險(xiǎn)

正常情況下,Redis 緩存單機(jī)就可支持十萬左右 QPS,并能通過集群部署提高整體負(fù)載能力。對(duì)于并發(fā)量一般的系統(tǒng),用 Redis 做緩存就足夠了。但是對(duì)于瞬時(shí)過高并發(fā)的請求,因?yàn)镽edis單線程原因會(huì)導(dǎo)致正常請求排隊(duì),或者因?yàn)闊狳c(diǎn)集中導(dǎo)致分片集群壓力過載而癱瘓,從而擊穿到DB引起服務(wù)器雪崩。

對(duì)應(yīng)用服務(wù)的風(fēng)險(xiǎn)

每個(gè)應(yīng)用在單位時(shí)間所能接受和處理的請求量是有限的,如果受到惡意請求的攻擊,讓惡意用戶獨(dú)自占用了大量請求處理資源,就會(huì)導(dǎo)致其他人畜無害的正常用戶的請求無法及時(shí)響應(yīng)。

因此,需要一套動(dòng)態(tài)熱Key 檢測機(jī)制,通過對(duì)需要檢測的熱Key規(guī)則進(jìn)行配置,實(shí)時(shí)監(jiān)聽統(tǒng)計(jì)熱Key數(shù)據(jù),當(dāng)無預(yù)期的熱點(diǎn)數(shù)據(jù)出現(xiàn)時(shí),第一時(shí)間發(fā)現(xiàn)他,并針對(duì)這些數(shù)據(jù)進(jìn)行特殊處理。如本地緩存、拒絕惡意用戶、接口限流 / 降級(jí)等。

3.2 如何進(jìn)行熱點(diǎn)探測?

首先我們要定義一下如何才能算是一個(gè)熱點(diǎn),我們知道熱點(diǎn)產(chǎn)生的條件是2個(gè):一個(gè)時(shí)間,一個(gè)流量。那么根據(jù)這個(gè)條件我們可以簡單定義一個(gè)規(guī)則:比如 1 秒內(nèi)訪問 1000 次的數(shù)據(jù)算是熱數(shù)據(jù),當(dāng)然這個(gè)數(shù)據(jù)需要根據(jù)具體的業(yè)務(wù)場景和過往數(shù)據(jù)進(jìn)行具體評(píng)估。

對(duì)于單機(jī)應(yīng)用,檢測熱數(shù)據(jù)很簡單,直接在本地為每個(gè)Key創(chuàng)建一個(gè)滑動(dòng)窗口計(jì)數(shù)器,統(tǒng)計(jì)單位時(shí)間內(nèi)的訪問總數(shù)(頻率),并通過一個(gè)集合存放檢測到的熱 Key。

7f270200-bb89-11ed-bfe3-dac502259ad0.png

而對(duì)于分布式應(yīng)用,對(duì)熱 Key 的訪問是分散在不同的機(jī)器上的,無法在本地獨(dú)立地進(jìn)行計(jì)算,因此,需要一個(gè)獨(dú)立的、集中的熱 Key 計(jì)算單元

我們可以簡單理解為:分布式應(yīng)用節(jié)點(diǎn)感知熱點(diǎn)規(guī)則配置,將熱點(diǎn)數(shù)據(jù)進(jìn)行上報(bào),工作節(jié)點(diǎn)進(jìn)行熱點(diǎn)數(shù)據(jù)統(tǒng)計(jì),對(duì)于符合閾值的熱點(diǎn)進(jìn)行推送給客戶端,應(yīng)用收到熱點(diǎn)信息進(jìn)行本地緩存等策略這五個(gè)步驟:

1. 熱點(diǎn)規(guī)則:配置熱Key的上報(bào)規(guī)則,圈出需要重點(diǎn)監(jiān)測的Key

2. 熱點(diǎn)上報(bào):應(yīng)用服務(wù)將自己的熱Key訪問情況上報(bào)給集中計(jì)算單元

3. 熱點(diǎn)統(tǒng)計(jì):收集各應(yīng)用實(shí)例上報(bào)的信息,使用滑動(dòng)窗口算法計(jì)算Key的熱度

4. 熱點(diǎn)推送:當(dāng)Key的熱度達(dá)到設(shè)定值時(shí),推送熱Key信息至所有應(yīng)用實(shí)例

5. 熱點(diǎn)緩存:各應(yīng)用實(shí)例收到熱Key信息后,對(duì)Key值進(jìn)行本地緩存(此步驟根據(jù)具體業(yè)務(wù)策略調(diào)整)

7fe7b8d8-bb89-11ed-bfe3-dac502259ad0.png

4

Burning

理解完熱點(diǎn)探測原理以后,我們來聊聊得物的熱點(diǎn)探測中間件Burning。

作為潮流互聯(lián)網(wǎng)電商平臺(tái),得物的電商業(yè)務(wù)高速發(fā)展,突發(fā)性的熱點(diǎn)數(shù)據(jù)不斷的沖擊著我們的系統(tǒng)服務(wù),比如大促秒殺,熱點(diǎn)商品,惡意攻擊等等。針對(duì)于這種突發(fā)性的大流量,單純的機(jī)器擴(kuò)容并不是一個(gè)有效的解決手段,我們需要一個(gè)集熱點(diǎn)探測,熱點(diǎn)感知,熱點(diǎn)數(shù)據(jù)推送,熱點(diǎn)數(shù)據(jù)預(yù)熱,熱點(diǎn)監(jiān)控分析等功能于一體的熱點(diǎn)探測中間件,因此Burning應(yīng)運(yùn)而生。

4.1 價(jià)值意義

Burning作為得物的熱點(diǎn)探測中間件,提供可供業(yè)務(wù)方接入的SDK包和管理臺(tái)規(guī)則配置,用于對(duì)熱點(diǎn)數(shù)據(jù)的實(shí)時(shí)性監(jiān)控,探測,操作和本地緩存等。主要解決了以下問題:

實(shí)時(shí)熱點(diǎn)感知:能實(shí)時(shí)監(jiān)控?zé)狳c(diǎn)數(shù)據(jù),包含熱Key,熱數(shù)據(jù),熱接口等,秒級(jí)上報(bào)集群統(tǒng)一計(jì)算

本地?cái)?shù)據(jù)預(yù)熱:對(duì)于特定場景可以通過動(dòng)態(tài)本地緩存配置,防止流量突增導(dǎo)致Redis或DB數(shù)據(jù)流量壓力過大導(dǎo)致系統(tǒng)雪崩

周期熱點(diǎn)統(tǒng)計(jì):對(duì)熱點(diǎn)數(shù)據(jù)進(jìn)行周期性統(tǒng)計(jì)分析,標(biāo)記出熱Key規(guī)則及分布比例等,可以幫助業(yè)務(wù)方進(jìn)行針對(duì)性優(yōu)化治理和營銷策略選擇

系統(tǒng)安全治理可以通過熱點(diǎn)Key探測分析,對(duì)于刷子用戶,問題IP,機(jī)器人和爬蟲進(jìn)行標(biāo)識(shí),可實(shí)時(shí)熔斷存在安全風(fēng)險(xiǎn)的請求,提高系統(tǒng)安全和可用性

4.2 關(guān)鍵指標(biāo)

為滿足高并發(fā)場景,熱點(diǎn)探測中間件Burning在設(shè)計(jì)的時(shí)候,重點(diǎn)關(guān)注了如下指標(biāo):

1.實(shí)時(shí)性熱點(diǎn)問題往往具備突發(fā)性,客戶端必須能夠?qū)崟r(shí)發(fā)現(xiàn)可疑熱Key并推送給計(jì)算單元進(jìn)行探測

2.高性能:熱點(diǎn)探測往往需要處理大量的熱點(diǎn)探測請求和熱點(diǎn)計(jì)算,因此熱點(diǎn)探測中間件的性能要求較高,才能滿足巨量的并發(fā)并有效降低成本

3.準(zhǔn)確性:熱點(diǎn)探測需要精準(zhǔn)的探測符合規(guī)則熱Key,實(shí)時(shí)監(jiān)聽規(guī)則的變化,正確的進(jìn)行熱Key上報(bào)和熱Key計(jì)算

4.一致性:熱點(diǎn)探測需要保證應(yīng)用實(shí)例的本地緩存熱Key一致,當(dāng)熱Key變更導(dǎo)致value失效時(shí),應(yīng)用需要同時(shí)進(jìn)行失效來保證數(shù)據(jù)一致性,不能出現(xiàn)數(shù)據(jù)錯(cuò)誤

5.可擴(kuò)展:熱點(diǎn)探測需要統(tǒng)計(jì)和計(jì)算的Key量級(jí)很大,而且存在突發(fā)流量的情況,統(tǒng)一計(jì)算集群需要具備水平擴(kuò)展的能力

80b1085a-bb89-11ed-bfe3-dac502259ad0.png

4.3 架構(gòu)設(shè)計(jì)

Burning的架構(gòu)設(shè)計(jì)遵循了以上熱點(diǎn)探測的技術(shù)原理,同時(shí)借鑒了jd-hotKey的設(shè)計(jì)思路,主要分為Burning-Admin、Burning-Worker、Burning-Config、Burning-Client四個(gè)模塊:

Burning-Admin (熱點(diǎn)探測管理臺(tái)):與Worker節(jié)點(diǎn)Netty長鏈接通信,提供不同維度的應(yīng)用管理和熱點(diǎn)規(guī)則配置,提供查詢熱點(diǎn)數(shù)據(jù)統(tǒng)計(jì),規(guī)則和熱點(diǎn)數(shù)據(jù)監(jiān)控大盤,提供工作集群信息查詢及客戶端節(jié)點(diǎn)信息查詢,提供本地緩存動(dòng)態(tài)配置及熱點(diǎn)信息實(shí)時(shí)通知

Burning-Worker(熱點(diǎn)集中計(jì)算單元):無狀態(tài)server端,與管理臺(tái)和客戶端進(jìn)行Netty長鏈接通信,獲取規(guī)則,滑動(dòng)窗口計(jì)算熱點(diǎn),將熱點(diǎn)記錄推送到管理臺(tái)展示和客戶端處理

Burning-Config(熱點(diǎn)配置中心):作為熱點(diǎn)、規(guī)則配置中心和注冊中心,將規(guī)則配置下發(fā)到Worker節(jié)點(diǎn)和客戶端,通過Raft算法進(jìn)行系統(tǒng)高可用一致性保證

Burning-Client(熱點(diǎn)客戶端SDK):與Worker節(jié)點(diǎn)建立Netty長鏈接通信,監(jiān)聽配置中心配置變化定時(shí)推送熱Key數(shù)據(jù),獲取熱Key推送本地內(nèi)緩存設(shè)置,與Redis-client無縫集成及其他ORM框架無縫集成

814d120e-bb89-11ed-bfe3-dac502259ad0.png

4.4 鏈路流程

熱點(diǎn)探測主要包含以下幾個(gè)主要流程:

用戶在管理后臺(tái)(Burning-Admin)進(jìn)行熱點(diǎn)規(guī)則配置并進(jìn)行熱點(diǎn)數(shù)據(jù)實(shí)時(shí)監(jiān)控

管理后臺(tái)(Burning-Admin)將規(guī)則配置信息上傳給配置中心(Burning-Config)

配置中心(Burning-Config)將熱點(diǎn)規(guī)則下發(fā)給客戶端(Buring-Client)和工作節(jié)點(diǎn)(Burning-Worker)

客戶端(Burning-Client)獲取到規(guī)則, 將指定規(guī)則的熱Key定時(shí)上報(bào)給工作節(jié)點(diǎn)(Burning-Worker)

工作節(jié)點(diǎn)(Burning-Worker)獲取到上報(bào)的熱Key后進(jìn)行滑動(dòng)時(shí)間窗口計(jì)算,對(duì)于滿足閾值的熱點(diǎn)推送給客戶端(Burning-Client)

客戶端(Burning-Client)拿到熱點(diǎn)數(shù)據(jù)后,進(jìn)行對(duì)應(yīng)的本地緩存配置

82580c12-bb89-11ed-bfe3-dac502259ad0.png

4.5 核心代碼

客戶端啟動(dòng)器ClientStarter,啟動(dòng)配置中心和注冊中心,Worker建連,注冊事件監(jiān)聽,設(shè)置app_name、port、caffeine緩存大小、cache配置、監(jiān)控配置等


public synchronized static void startPipeline(BurningCommonProperties burningCommonProperties) {


    if (STARTED.get() == Boolean.FALSE) {
        DwLogger.info("start pipeline");
        // 設(shè)置參數(shù)上下文
        setToContext(burningCommonProperties);
        // 配置中心啟動(dòng)
        EtcdConfigFactory.buildConfigCenter(burningCommonProperties.getConfigServer());
        ConfigStarter starter = EtcdConfigStarter.getInstance();
        starter.start();
        // 注冊中心啟動(dòng)
        RegisterFactory.buildRegisterCenter(burningCommonProperties);
        RegisterStarter registerStarter = RegisterStarter.getInstance();
        registerStarter.start();
        // 熱點(diǎn)探測啟動(dòng)
        DetectFactory.startDetect(burningCommonProperties.getPushPeriod());
        // 開啟worker重連器
        WorkerRetryConnector.retryConnectWorkers();
        // 注冊事件監(jiān)聽
        registEventBus();
        // 開啟監(jiān)控
        MetricsFactory.startMetrics();
        STARTED.set(Boolean.TRUE);
    }


}

客戶端進(jìn)行熱Key判斷,如果符合規(guī)則就上報(bào)給Worker節(jié)點(diǎn)計(jì)算,同時(shí)進(jìn)行統(tǒng)計(jì)計(jì)數(shù)

public static Object dynamicGetValue(String key, KeyType keyType) {
    try {
        //如果沒有為該key配置規(guī)則,就不用上報(bào)key
        Boolean dynamicRule = dynamicRule(key);
        if (dynamicRule == null) {
            return null;
        }
        Object userValue = null;


        ValueModel value = getValueSimple(key);


        if (value == null) {
            HotKeyPusher.push(key, keyType);
        } else {
            //臨近過期了,也發(fā)
            if (isNearExpire(value)) {
                HotKeyPusher.push(key, keyType);
            }
            Object object = value.getValue();
            //如果是默認(rèn)值,也返回null
            if (object instanceof Integer && Constant.MAGIC_NUMBER == (int) object) {
                userValue = null;
            } else if (Boolean.FALSE.equals(dynamicRule)) {
                userValue = null;
            } else {
                userValue = object;
            }
        }


        //統(tǒng)計(jì)計(jì)數(shù)
        MetricsFactory.metrics(new KeyHotModel(key, value != null));


        return userValue;
    } catch (Exception e) {
        DwLogger.error(DwHotKeyStore.class, "get value error");
        return null;
    }
}

Worker節(jié)點(diǎn)啟動(dòng)nettyServer,用于各個(gè)業(yè)務(wù)服務(wù)實(shí)例進(jìn)行長連接,接收客戶端上報(bào)數(shù)據(jù)

public void startNettyServer(int port) throws Exception {
    //boss單線程
    EventLoopGroup bossGroup = new NioEventLoopGroup(1);
    //worker節(jié)點(diǎn)組
    EventLoopGroup WorkerGroup = new NioEventLoopGroup(CpuNum.WorkerCount());
    try {
        ServerBootstrap bootstrap = new ServerBootstrap();
        bootstrap.group(bossGroup, WorkerGroup)
                .channel(NioServerSocketChannel.class)
                .handler(new LoggingHandler(LogLevel.INFO))
                .option(ChannelOption.SO_BACKLOG, 1024)
                //保持長連接
                .childOption(ChannelOption.SO_KEEPALIVE, true)
                //出來網(wǎng)絡(luò)io事件,如記錄日志、對(duì)消息編解碼等
                .childHandler(new ChildChannelHandler());
        //綁定端口,同步等待成功
        ChannelFuture future = bootstrap.bind(port).sync();
        Runtime.getRuntime().addShutdownHook(new Thread(() -> {
            bossGroup.shutdownGracefully (1000, 3000, TimeUnit.MILLISECONDS);
            WorkerGroup.shutdownGracefully (1000, 3000, TimeUnit.MILLISECONDS);
        }));
        //等待服務(wù)器監(jiān)聽端口關(guān)閉
        future.channel().closeFuture().sync();
    } catch (Exception e) {
        DwLogger.error("netty server start error.", e);
    } finally {
        //優(yōu)雅退出,釋放線程池資源
        bossGroup.shutdownGracefully();
        WorkerGroup.shutdownGracefully();
    }
}

Worker節(jié)點(diǎn)通過監(jiān)聽客戶端上報(bào),異步消費(fèi)隊(duì)列Client消息

public void beginConsume() {
    while (true) {
        try {
            HotKeyModel model = QUEUE.take();
            if (model.isRemove()) {
                iKeyListener.removeKey(model, KeyEventOriginal.CLIENT);
            } else {
                iKeyListener.newKey(model, KeyEventOriginal.CLIENT);
            }
            //處理完畢,將數(shù)量加1
            totalDealCount.increment();
        } catch (Exception e) {
            DwLogger.error("consumer error.", e);
        }
    }
}

如果是新增一個(gè)Key,就生成滑動(dòng)窗口,基于時(shí)間窗口數(shù)據(jù)判斷是否熱Key

@Override
public void newKey(HotKeyModel hotKeyModel, KeyEventOriginal original) {
    //cache里的key
    String key = buildKey(hotKeyModel);
    String name = StringUtils.isBlank(hotKeyModel.getGroup()) ? hotKeyModel.getAppName() : hotKeyModel.getGroup();


    //判斷是不是剛熱不久
    Object o = hotCache.getIfPresent(key);
    if (o != null) {
        return;
    }
    SlidingWindow slidingWindow = checkWindow(hotKeyModel, key, name);
    //看看hot沒
    boolean hot = slidingWindow.addCount(hotKeyModel.getCount());


    if (!hot) {
        //如果沒hot,重新put,cache會(huì)自動(dòng)刷新過期時(shí)間
        CaffeineCacheHolder.getCache(name).put(key, slidingWindow);
    } else {
        hotCache.put(key, 1);


        //刪掉該key
        CaffeineCacheHolder.getCache(name).invalidate(key);


        //開啟推送
        hotKeyModel.setCreateTime(SystemClock.now());


        //當(dāng)開關(guān)打開時(shí),打印日志。大促時(shí)關(guān)閉日志,就不打印了
        if (ConfigStarter.LOGGER_ON) {
            DwLogger.info(NEW_KEY_EVENT + hotKeyModel.getKey());
        }


        //分別推送到各client和etcd
        for (IPusher pusher : iPushers) {
            pusher.push(hotKeyModel);
        }


    }


}

如果是刪除一個(gè)Key,這里刪除包含客戶端發(fā)消息刪除,本地線程掃描過期Key和管理臺(tái)刪除

@Override
public void removeKey(HotKeyModel hotKeyModel, KeyEventOriginal original) {
    //cache里的key
    String key = buildKey(hotKeyModel);
    String name = StringUtils.isBlank(hotKeyModel.getGroup()) ? hotKeyModel.getAppName() : hotKeyModel.getGroup();
    hotCache.invalidate(key);
    CaffeineCacheHolder.getCache(name).invalidate(key);


    //推送所有client刪除
    hotKeyModel.setCreateTime(SystemClock.now());
    DwLogger.info(DELETE_KEY_EVENT + hotKeyModel.getKey());


    for (IPusher pusher : iPushers) {
        pusher.remove(hotKeyModel);
    }


}

Worker計(jì)算完成后將結(jié)果異步推送給Client,通過app進(jìn)行分組批量推送

@PostConstruct
public void batchPushToClient() {
    AsyncPool.asyncDo(() -> {
        while (true) {
            try {
                List tempModels = new ArrayList<>();
                //每10ms推送一次
                Queues.drain(hotKeyStoreQueue, tempModels, 10, 10, TimeUnit.MILLISECONDS);
                if (CollectionUtil.isEmpty(tempModels)) {
                    continue;
                }


                Map> allAppHotKeyModels = Maps.newHashMap();
                Map> allGroupHotKeyModels = Maps.newHashMap();


                //拆分出每個(gè)app的熱key集合,按app分堆
                for (HotKeyModel hotKeyModel : tempModels) {
                    if (StringUtils.isNotBlank(hotKeyModel.getGroup())) {
                        List groupModels = allGroupHotKeyModels.computeIfAbsent(hotKeyModel.getGroup(), (key) -> new ArrayList<>());
                        groupModels.add(hotKeyModel);
                    } else {
                        List oneAppModels = allAppHotKeyModels.computeIfAbsent(hotKeyModel.getAppName(), (key) -> new ArrayList<>());
                        oneAppModels.add(hotKeyModel);
                    }
                }


                CustomizedMetricsProcessor processor = CustomizedMetricsProcessor.builder(MetricsConstant.BURNING_NETTY_OUT).build();


                // group hot key push
                pushGroup(processor, allGroupHotKeyModels);


                // app hot key push
                pushApp(processor, allAppHotKeyModels);


            } catch (Exception e) {
                DwLogger.error("push to client error.", e);
            }
        }
    });
}

4.6 最佳實(shí)踐

Burning提供了2種使用方式,一是通過原生方法調(diào)用,二是通過聲明式注解@EnableBurning , 以下對(duì)使用注解進(jìn)行熱點(diǎn)探測的部分場景提供最佳實(shí)踐:

1. 進(jìn)行熱點(diǎn)判斷,用于熱點(diǎn)攔截和自定義處理實(shí)現(xiàn)

@Component
public class Cache {
    @EnableBurning(prefix = "hot_Key_", cache = false, hitHandler = ExceptionHitHandler.class)
    public String getResult2(String Key) {
        return "這是一個(gè)測試結(jié)果" + Key;
    }
}

2. 命中熱點(diǎn)規(guī)則處理類,可進(jìn)行自定義實(shí)現(xiàn)hitHandler接口(注意cache=false)

public class ExceptionHitHandler implements HitHandler {
   @Override
   public Object handle(String Key, ProceedingJoinPoint joinPoint) {
       //此處可自定義實(shí)現(xiàn)
      throw new RuntimeException("對(duì)不起,您沒有權(quán)限訪問: " + Key);
   }
}

3. 用于Redis緩存熱點(diǎn)探測

@Component
public class Cache {


    @Resource
    private RedisTemplate RedisTemplate;


    @EnableBurning
    public String getResult(String Key) {
        return RedisTemplate.opsForValue().get(Key);
    }
}

4. 用于MySQL熱數(shù)據(jù)緩存

@Repository
public class SmsSignRepo {


   @Autowired
   private SmsSignMapper smsSignMapper;


   @EnableBurning(prefix = "SMS_SIGN", dynamic = false, KeyType = DATABASE_Key)
   public List getAll() {
      Example example = new Example(SmsSign.class);
      Example.Criteria criteria = example.createCriteria();
      criteria.andEqualTo("status", 1);
      return smsSignMapper.selectByExample(example);
   }
}

4.7 性能表現(xiàn)

4.7.1 Worker節(jié)點(diǎn)性能壓測

上游40個(gè)測試調(diào)用實(shí)例共同調(diào)用的場景下,并發(fā)數(shù)800,遞進(jìn)壓測

83923364-bb89-11ed-bfe3-dac502259ad0.jpg

壓測結(jié)果:1個(gè)4C8G工作節(jié)點(diǎn)每秒可平穩(wěn)處理約15W個(gè)key的熱點(diǎn)探測,成功率大于99.999%,worker節(jié)點(diǎn)CPU平均占用為80%,內(nèi)存占用60%

4.7.2 Client業(yè)務(wù)應(yīng)用性能壓測

DB場景壓測

Client配置為4C8G,120個(gè)并發(fā)請求,壓測時(shí)長10min

原生未接入Burning的DB操作接口場景

83bf441c-bb89-11ed-bfe3-dac502259ad0.png

壓測結(jié)果:未接入burning,處理總請求數(shù)約112萬,平均TPS約1500,平均RT約63MS。CPU在壓測滿載情況下100%,內(nèi)存平均使用48%

接入Burning的DB操作接口場景

83e320e4-bb89-11ed-bfe3-dac502259ad0.jpg

壓測結(jié)果:接入burning后,處理總請求數(shù)457萬(對(duì)比未接入Burning增加345萬),平均TPS約5800(對(duì)比未接入Burning增加4300),平均RT約8MS(對(duì)比未接入Burning下降55MS)。CPU在壓測滿載情況下100%,內(nèi)存平均使用50%(對(duì)比未接入上升2%,本地緩存消耗

Redis場景壓測

Client配置為4C8G,120個(gè)并發(fā)請求,壓測時(shí)長10min

原生未接入Burning的Redis操作接口場景

8403206a-bb89-11ed-bfe3-dac502259ad0.png

壓測結(jié)果:未接入burning,處理總請求數(shù)約298萬,平均TPS約3800,平均RT約14MS。CPU在壓測滿載情況下100%,內(nèi)存平均使用48%

已接入Burning的Redis操作接口場景

8435828a-bb89-11ed-bfe3-dac502259ad0.png

壓測結(jié)果:已接入burning,處理總請求數(shù)約443萬(對(duì)比未接入增加145萬),平均TPS約5700(對(duì)比未接入上升1900),平均RT約8MS(對(duì)比未接入下降6ms)。CPU在壓測滿載情況下100%,內(nèi)存平均使用48%,基本持平

4.7.3 壓測報(bào)告

Burning工作節(jié)點(diǎn)單機(jī)每秒處理15萬個(gè)key的探測請求,CPU穩(wěn)定在80%左右,基本無任何異常

客戶端應(yīng)用接入burning后,對(duì)應(yīng)用實(shí)例本身CPU負(fù)載基本無影響,內(nèi)存占用上升主要取決于指定的本地緩存大小,接入后接口性能提升明顯,QPS明顯上升,RT明顯下降

5

總結(jié)

熱點(diǎn)問題在互聯(lián)網(wǎng)場景中屢屢出現(xiàn),特別是電商業(yè)務(wù)的需求場景,例如對(duì)于大促期間或者活動(dòng)搶購期間的某個(gè)爆品,可能會(huì)出現(xiàn)在幾秒時(shí)間內(nèi)流入大量的流量,由于商品數(shù)據(jù)在Redis cluster場景下會(huì)按照hash規(guī)則被存放在某個(gè)Redis分片上,那么這個(gè)瞬間流量也有可能出現(xiàn)打掛Redis分片,導(dǎo)致系統(tǒng)雪崩。所以我們要善于利用熱點(diǎn)探測中間件進(jìn)行熱Key探測,通過預(yù)置本地緩存解決突發(fā)流量導(dǎo)致的系統(tǒng)瓶頸,也能通過熱點(diǎn)數(shù)據(jù)監(jiān)控分析進(jìn)行針對(duì)性的系統(tǒng)調(diào)優(yōu)。

得物熱點(diǎn)探測組件Burning上線至今,支持了數(shù)十個(gè)交易核心鏈路服務(wù),在滿足基礎(chǔ)熱點(diǎn)探測的前提下,Burning還支持本地緩存壓測標(biāo)/染色標(biāo)識(shí)別能力,客戶端本地Ecache/Caffeine緩存模式選擇,熱點(diǎn)規(guī)則Group聚合統(tǒng)計(jì)等擴(kuò)展能力。應(yīng)用服務(wù)接入Burning后對(duì)于熱點(diǎn)數(shù)據(jù)探測及數(shù)據(jù)獲取性能顯著提高,通過預(yù)熱&實(shí)時(shí)本地緩存,極大的降低了下層緩存集群和數(shù)據(jù)庫的負(fù)載壓力,為業(yè)務(wù)服務(wù)的健康運(yùn)作保駕護(hù)航。





審核編輯:劉清

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報(bào)投訴
  • 機(jī)器人
    +關(guān)注

    關(guān)注

    210

    文章

    27839

    瀏覽量

    204594
  • MySQL
    +關(guān)注

    關(guān)注

    1

    文章

    789

    瀏覽量

    26283
  • QPS
    QPS
    +關(guān)注

    關(guān)注

    0

    文章

    24

    瀏覽量

    8777
  • Redis
    +關(guān)注

    關(guān)注

    0

    文章

    368

    瀏覽量

    10780

原文標(biāo)題:得物熱點(diǎn)探測技術(shù)架構(gòu)設(shè)計(jì)與實(shí)踐

文章出處:【微信號(hào):OSC開源社區(qū),微信公眾號(hào):OSC開源社區(qū)】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。

收藏 人收藏

    評(píng)論

    相關(guān)推薦

    聯(lián)網(wǎng)(IoT -Oriented)架構(gòu)設(shè)計(jì)原則和功能特點(diǎn)

    數(shù)據(jù)基礎(chǔ)設(shè)施正在從云原生(Cloud-Native) 向面向聯(lián)網(wǎng)(IoT-Oriented)架構(gòu)演進(jìn)?;诖?,我們總結(jié)了面向聯(lián)網(wǎng) (IoT -Oriented)架構(gòu)的設(shè)計(jì)原則和功能
    發(fā)表于 07-11 16:54 ?3030次閱讀

    軟件架構(gòu)設(shè)計(jì)教程

    軟件架構(gòu)設(shè)計(jì)教程
    發(fā)表于 09-26 15:27

    汽車電子電氣架構(gòu)設(shè)計(jì)及優(yōu)化措施

    的知識(shí),開發(fā)周期也延長到了三到五年。所以,基于市場需求的汽車電子電氣架構(gòu)設(shè)計(jì)及其優(yōu)化措施需要引起生產(chǎn)廠家的高度重視。1.汽車電子電氣架構(gòu)設(shè)計(jì)的主要優(yōu)化工具分析1.1 數(shù)據(jù)庫基準(zhǔn)數(shù)據(jù)庫幾乎囊括了世界上
    發(fā)表于 10-18 22:10

    熱點(diǎn)探測器-購線網(wǎng)

    HSD-T熱點(diǎn)探測器是美國近30年來現(xiàn)狀溫度傳感器技術(shù)發(fā)展的主要成果。它利用熱電效應(yīng)原理能夠連續(xù)生產(chǎn)與其長度所及范圍內(nèi)最熱點(diǎn)溫度相應(yīng)的毫伏信號(hào),可用來連續(xù)
    發(fā)表于 06-28 09:28

    阿里云Overlay的SDN 實(shí)踐: 架構(gòu)設(shè)計(jì)與產(chǎn)品實(shí)現(xiàn)

    摘要: 這篇文章介紹了阿里云在云上 overlay 網(wǎng)絡(luò)的SDN實(shí)踐, 包括了 overlay 架構(gòu)的介紹, 建設(shè) overlay 時(shí)遇到的挑戰(zhàn), 以及基于 overlay 的產(chǎn)品.在不久前
    發(fā)表于 04-27 16:52

    阿里云Overlay的SDN 實(shí)踐: 架構(gòu)設(shè)計(jì)與產(chǎn)品實(shí)現(xiàn)

    摘要: 這篇文章介紹了阿里云在云上 overlay 網(wǎng)絡(luò)的SDN實(shí)踐, 包括了 overlay 架構(gòu)的介紹, 建設(shè) overlay 時(shí)遇到的挑戰(zhàn), 以及基于 overlay 的產(chǎn)品.在不久前
    發(fā)表于 04-27 16:52

    STM32軟件架構(gòu)設(shè)計(jì)的意義

    STM32軟件架構(gòu)1、架構(gòu)設(shè)計(jì)的意義(1)應(yīng)用代碼邏輯清晰,且避免代碼冗余;(2)代碼通用性,方便軟件高速、有效的移植;(3)各功能獨(dú)立,低耦合高內(nèi)聚;2、總體架構(gòu)圖3、結(jié)構(gòu)層說明4、遵循規(guī)則5、優(yōu)劣評(píng)估6、STM32實(shí)例說明
    發(fā)表于 08-04 07:23

    聯(lián)網(wǎng)技術(shù)架構(gòu)

    聯(lián)網(wǎng)技術(shù)架構(gòu)1. 顯示端JavaScript,以java語言為主的Web框架等Spring全家桶,Android,IOS,微信公眾號(hào),直接板載液晶顯示屏,觸摸屏,移植性比較好的QT2. 通信
    發(fā)表于 08-20 07:10

    軟件架構(gòu)設(shè)計(jì)的三個(gè)維度

    架構(gòu)設(shè)計(jì)是一個(gè)非常大的話題,不管寫幾篇文章,接觸到的始終只是冰山一角,更多的是實(shí)踐中去體會(huì)。這篇文章主要介紹面向?qū)ο驩O、面向方面AOP和面向服務(wù)SOA這三個(gè)要素在架構(gòu)設(shè)計(jì)中
    發(fā)表于 06-22 10:09 ?1396次閱讀
    軟件<b class='flag-5'>架構(gòu)設(shè)</b>計(jì)的三個(gè)維度

    基于ARMCortex_M3核的SoC架構(gòu)設(shè)計(jì)及性能分析

    基于ARMCortex_M3核的SoC架構(gòu)設(shè)計(jì)及性能分析
    發(fā)表于 09-29 09:26 ?18次下載
    基于ARMCortex_M3核的SoC<b class='flag-5'>架構(gòu)設(shè)</b>計(jì)及性能<b class='flag-5'>分析</b>

    如何使用Autosar的進(jìn)行整車電子電氣架構(gòu)設(shè)計(jì)詳細(xì)方法概述

    提出一種基于Autosar理念的整車電子電氣架構(gòu)設(shè)計(jì)方法。通過整車需求定義、原子邏輯單元搭建實(shí)現(xiàn)整車功能邏輯的實(shí)體化,然后利用模型設(shè)計(jì)分析方法,將功能需求分配到各個(gè)電子控制單元,利用
    發(fā)表于 11-01 08:00 ?43次下載
    如何使用Autosar的進(jìn)行整車電子電氣<b class='flag-5'>架構(gòu)設(shè)</b>計(jì)詳細(xì)方法概述

    SWE.2的軟件架構(gòu)設(shè)計(jì)

    過程ID:SWE.2 過程名稱:軟件架構(gòu)設(shè)計(jì) 過程目的:軟件架構(gòu)設(shè)計(jì)過程目的是建立一個(gè)架構(gòu)設(shè)計(jì),識(shí)別哪些軟件需求應(yīng)該分配給軟件的哪些要素,并根據(jù)已定義的標(biāo)準(zhǔn)評(píng)估軟件架構(gòu)設(shè)計(jì)。 ? 過程
    的頭像 發(fā)表于 01-11 10:36 ?2647次閱讀

    汽車電子電氣架構(gòu)設(shè)計(jì)中控制器融合的分析和參考案例

    隨著汽車智能化、網(wǎng)聯(lián)化的發(fā)展,整車電器功能愈加豐富,對(duì)電子電氣架構(gòu)的設(shè)計(jì)提出了更高的要求。文章綜述了汽車電子電氣架構(gòu)的開發(fā)流程和發(fā)展趨勢,并為架構(gòu)設(shè)計(jì)中的控制器融合提供了分析方法和參考
    的頭像 發(fā)表于 10-19 15:50 ?2064次閱讀

    架構(gòu)與微架構(gòu)設(shè)計(jì)

    下面將從芯片的架構(gòu)設(shè)計(jì)、微架構(gòu)設(shè)計(jì)、使用設(shè)計(jì)文檔、設(shè)計(jì)分區(qū)、時(shí)鐘域和時(shí)鐘組、架構(gòu)調(diào)整與性能改進(jìn)、處理器微架構(gòu)設(shè)計(jì)策略等角度進(jìn)行說明,并以視頻H.264編碼器設(shè)計(jì)為例。
    的頭像 發(fā)表于 05-08 10:42 ?1064次閱讀
    <b class='flag-5'>架構(gòu)</b>與微<b class='flag-5'>架構(gòu)設(shè)</b>計(jì)

    商城庫存系統(tǒng)中心架構(gòu)設(shè)計(jì)與實(shí)踐案例

    本文探討的vivo官方商城庫存架構(gòu)設(shè)計(jì),從整個(gè)vivo大電商庫存架構(gòu)來看,vivo官方商城庫存系統(tǒng)涉及銷售層內(nèi)部架構(gòu)以及銷售層與調(diào)度層的交互。
    發(fā)表于 08-30 10:59 ?1297次閱讀
    商城庫存系統(tǒng)中心<b class='flag-5'>架構(gòu)設(shè)</b>計(jì)與<b class='flag-5'>實(shí)踐</b>案例