Sentinel熱點(diǎn)參數(shù)驗(yàn)證

一、熱點(diǎn)

熱點(diǎn)就是訪問(wèn)非常頻繁的參數(shù),例如商城系統(tǒng)中首頁(yè)的數(shù)據(jù);熱點(diǎn)參數(shù)就比如是商城系統(tǒng)商品的id。
那么 Sentinel 是怎么知道哪些參數(shù)是熱點(diǎn),哪些參數(shù)不是熱點(diǎn)的呢?Sentinel 利用 LRU 策略,結(jié)合底層的滑動(dòng)窗口機(jī)制來(lái)實(shí)現(xiàn)熱點(diǎn)參數(shù)統(tǒng)計(jì)。LRU 策略可以統(tǒng)計(jì)單位時(shí)間內(nèi),最近最常訪問(wèn)的熱點(diǎn)參數(shù),而滑動(dòng)窗口機(jī)制可以幫助統(tǒng)計(jì)每個(gè)參數(shù)的 qps。
說(shuō)簡(jiǎn)單點(diǎn)就是,Sentinel 會(huì)先檢查出提交過(guò)來(lái)的參數(shù),哪些是熱點(diǎn)的參數(shù),然后在應(yīng)用熱點(diǎn)參數(shù)的限流規(guī)則,將qps 超過(guò)設(shè)定閾值的請(qǐng)求給 block 掉,整個(gè)過(guò)程如下圖所示:

image.png

二、如何應(yīng)用

1、引入依賴

<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-parameter-flow-control</artifactId>
    <version>x.y.z</version>
</dependency>

2、定義資源

使用熱點(diǎn)限流時(shí),定義資源和普通限流的操作方式是一致的。例如我們可以定義資源名為:

/**
     * 資源名
     */
    private static String RESOURCE_NAME = "freqParam";

3、定義規(guī)則

定義好資源之后,就可以來(lái)定義規(guī)則了,我們還是先用簡(jiǎn)單的硬編碼的方式來(lái)演示,實(shí)際的使用過(guò)程中還是要通過(guò)控制臺(tái)來(lái)定義規(guī)則的。
熱點(diǎn)參數(shù)的規(guī)則是通過(guò) ParamFlowRule 來(lái)定義的,跟流控的規(guī)則類 FlowRule 差不多,具體的屬性如下表所示:

image.png

定義好規(guī)則之后,可以通過(guò) ParamFlowRuleManager 的 loadRules 方法更新熱點(diǎn)參數(shù)規(guī)則,如下所示:

private void init() {
        // 定義熱點(diǎn)限流的規(guī)則,對(duì)第一個(gè)參數(shù)設(shè)置 qps 限流模式,閾值為5
        ParamFlowRule rule = new ParamFlowRule(RESOURCE_NAME)
                .setParamIdx(0)
                .setGrade(RuleConstant.FLOW_GRADE_QPS)
                .setCount(5);
        ParamFlowRuleManager.loadRules(Collections.singletonList(rule));
    }

4、埋點(diǎn)

我們定義好資源,也定義好規(guī)則了,最后一步就是在代碼中埋點(diǎn)來(lái)使應(yīng)用熱點(diǎn)限流的規(guī)則了。
那么如何傳入對(duì)應(yīng)的參數(shù)來(lái)讓 Sentinel 進(jìn)行統(tǒng)計(jì)呢?我們可以通過(guò) SphU 類里面幾個(gè) entry 重載方法來(lái)傳入
其中最后的一串 args 就是要傳入的參數(shù),有多個(gè)就按照次序依次傳入。
還是通過(guò)一個(gè)簡(jiǎn)單的 Controller 方法來(lái)進(jìn)行埋點(diǎn),如下所示:

/**
     * 熱點(diǎn)參數(shù)限流
     * 構(gòu)造不同的uid的值,并且以不同的頻率來(lái)請(qǐng)求該方法,查看效果
     */
    @GetMapping("/freqParamFlow")
    public String freqParamFlow(@RequestParam("uid") Long uid, @RequestParam("ip") Long ip) {
        Entry entry = null;
        String retVal;
        try{
            // 只對(duì)參數(shù) uid 的值進(jìn)行限流,參數(shù) ip 的值不進(jìn)行限制
            entry = SphU.entry(RESOURCE_NAME, EntryType.IN,1,uid);
            retVal = "passed";
        }catch(BlockException e){
            retVal = "blocked";
        }finally {
            if(entry!=null){
                entry.exit();
            }
        }
        return retVal;
    }

5、查看效果

image.png
image.png

sentinel控制臺(tái)展示結(jié)果:

image.png

完整代碼:

package com.gzf.sentinel.controller;

import com.alibaba.csp.sentinel.Entry;
import com.alibaba.csp.sentinel.EntryType;
import com.alibaba.csp.sentinel.SphU;
import com.alibaba.csp.sentinel.cluster.client.config.ClusterClientAssignConfig;
import com.alibaba.csp.sentinel.cluster.client.config.ClusterClientConfig;
import com.alibaba.csp.sentinel.cluster.client.config.ClusterClientConfigManager;
import com.alibaba.csp.sentinel.datasource.ReadableDataSource;
import com.alibaba.csp.sentinel.datasource.nacos.NacosDataSource;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.alibaba.csp.sentinel.slots.block.RuleConstant;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.FlowRuleManager;
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRule;
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowRuleManager;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.util.Collections;
import java.util.List;

/**
 * @program: sentinel-tutorial
 * @description: 集群熱點(diǎn)參數(shù)限流規(guī)則
 * @author: Gaozf
 * @create: 2020-07-24 16:58
 **/
@RestController
public class ParamFlowController {
    private static String RESOURCE_NAME = "freqParam";

    private static final String APP_NAME = "single";

    private static final String REMOTE_ADDRESS = "localhost";
    private static final String GROUP_ID = "DEFAULT_GROUP";

    private static final String PARAM_FLOW_POSTFIX = "-param-rules";

    public ParamFlowController(){
        //registerClusterFlowRuleProperty();
        init();
    }

    private void init() {
        // 定義熱點(diǎn)限流的規(guī)則,對(duì)第一個(gè)參數(shù)設(shè)置 qps 限流模式,閾值為5
        ParamFlowRule rule = new ParamFlowRule(RESOURCE_NAME)
                .setParamIdx(0)
                .setGrade(RuleConstant.FLOW_GRADE_QPS)
                .setCount(5);
        ParamFlowRuleManager.loadRules(Collections.singletonList(rule));
    }
    /**
     * 注冊(cè)動(dòng)態(tài)規(guī)則Property
     * 當(dāng)client與Server連接中斷,退化為本地限流時(shí)需要用到的該規(guī)則
     */
    private void registerClusterFlowRuleProperty(){
        // 使用 Nacos 數(shù)據(jù)源作為配置中心,需要在 REMOTE_ADDRESS 上啟動(dòng)一個(gè) Nacos 的服務(wù)
        ReadableDataSource<String, List<FlowRule>> ds = new NacosDataSource<>(REMOTE_ADDRESS, GROUP_ID, APP_NAME+PARAM_FLOW_POSTFIX,
                source -> JSON.parseObject(source, new TypeReference<List<FlowRule>>() {}));
        // 為集群客戶端注冊(cè)動(dòng)態(tài)規(guī)則源
        FlowRuleManager.register2Property(ds.getProperty());
    }

    /**
     * 熱點(diǎn)參數(shù)限流
     * 構(gòu)造不同的uid的值,并且以不同的頻率來(lái)請(qǐng)求該方法,查看效果
     */
    @GetMapping("/freqParamFlow")
    public String freqParamFlow(@RequestParam("uid") Long uid, @RequestParam("ip") Long ip) {
        Entry entry = null;
        String retVal;
        try{
            // 只對(duì)參數(shù) uid 的值進(jìn)行限流,參數(shù) ip 的值不進(jìn)行限制
            entry = SphU.entry(RESOURCE_NAME, EntryType.IN,1,uid);
            retVal = "passed";
        }catch(BlockException e){
            retVal = "blocked";
        }finally {
            if(entry!=null){
                entry.exit();
            }
        }
        return retVal;
    }

    @GetMapping("/freqParamFlowTwo")
    public String freqParamFlowTwo(@RequestParam("uid") Long uid, @RequestParam("ip") Long ip) {
        Entry entry = null;
        String retVal;
        try{
            // 只對(duì)參數(shù) uid 的值進(jìn)行限流,參數(shù) ip 的值不進(jìn)行限制
            entry = SphU.entry(RESOURCE_NAME, EntryType.IN,1);
            retVal = "passed";
        }catch(BlockException e){
            retVal = "blocked";
        }finally {
            if(entry!=null){
                entry.exit();
            }
        }
        return retVal;
    }
}

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容