天天看點

SpringCloudAlibaba項目之Sentinel流量控制

SpringCloudAlibaba随筆目錄

一、SpringCloudAlibaba項目之父工程搭建

二、SpringCloudAlibaba項目之Nacos搭建及服務注冊

三、SpringCloudAlibaba項目之生産者與消費者

四、SpringCloudAlibaba項目之Ribbon負載均衡

五、SpringCloudAlibaba項目之OpenFeign遠端調用

六、SpringCloudAlibaba項目之Nacos-config配置中心

七、SpringCloudAlibaba項目之Sentinel流量控制

八、SpringCloudAlibaba項目之Seata分布式事務

九、SpringCloudAlibaba項目之GateWay網關

十、SpringCloudAlibaba項目之SkyWalking鍊路追蹤

SpringCloudAlibaba項目之Sentinel流量控制

1、Sentinel簡介

  Sentinel是阿裡開源的項目,提供了流量控制、熔斷降級、系統負載保護等多個次元來保障服務之間的穩定性。

官網:https://github.com/alibaba/Sentinel/wiki

Sentinel主要特性:

SpringCloudAlibaba項目之Sentinel流量控制

 2、Sentinel與Hystrix的差別

  

SpringCloudAlibaba項目之Sentinel流量控制

關于Sentinel與Hystrix的差別見:https://yq.aliyun.com/articles/633786/

總體來說:

  Hystrix常用的線程池隔離會造成線程上下切換的overhead比較大;Hystrix使用的信号量隔離對某個資源調用的并發數進行控制,效果不錯,但是無法對慢調用進行自動降級;Sentinel通過并發線程數的流量控制提供信号量隔離的功能;

此外,Sentinel支援的熔斷降級次元更多,可對多種名額進行流控、熔斷,且提供了實時監控和控制台,功能更為強大。

  Sentinel 的所有規則都可以在記憶體态中動态地查詢及修改,修改之後立即生效。同時 Sentinel 也提供相關 API,供您來定制自己的規則政策。

Sentinel 支援以下幾種規則:流量控制規則、熔斷降級規則、系統保護規則、來源通路控制規則 和 熱點參數規則。

3、使用Sentinel 核心庫體驗流量控制

  我們先使用springboot應用程式,快速搭建使用,體驗流控效果。

 官網:https://github.com/alibaba/Sentinel/wiki/%E6%96%B0%E6%89%8B%E6%8C%87%E5%8D%97#%E5%85%AC%E7%BD%91-demo

SpringCloudAlibaba項目之Sentinel流量控制
 pom.xml中添加依賴

<!-- springweb 啟動依賴 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- sentinel 核心庫 -->
<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-core</artifactId>
    <version>1.8.2</version>
</dependency>      
SentinelController接口:      
/**
 * Sentinel接口流控測試
 */
@RestController
public class SentinelController {
    public static final String RESOURCE_NAME = "sentinel";//資源名

    //進行Sentinel流控
    @RequestMapping(value = "/sentinel")
    public String sentinelTest(){
        Entry entry = null;
        try {
            //sentinel針對資源進行限制
            entry = SphU.entry(RESOURCE_NAME);
            //被保護的業務邏輯
            String str = "Sentinel接口正常";
            System.out.println("====" + str + "====");
            return str;
        } catch (BlockException e) {
            e.printStackTrace();
            //資源通路阻止,被限流或被降級
            //進行相應的處理操作
            System.out.println("Sentinel接口被流控了");
            return "Sentinel接口被流控了";
        }catch (Exception e){
            // 若需要配置降級規則,需要通過這種方式記錄業務異常
            Tracer.traceEntry(e,entry);
        }finally {
            if(entry != null){
                entry.exit();
            }
        }
        return null;
    }

    /**
     * 定義規則
     *
     * spring的初始化方法
     */
    @PostConstruct
    private static void initFlowRules(){
        //流控規則
        List<FlowRule> rules = new ArrayList<>();
        //流控
        FlowRule rule = new FlowRule();
        //設定受保護的資源
        rule.setResource(RESOURCE_NAME);
        // 設定流控規則 QPS
        rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
        //設定受保護資源的門檻值
        // Set limit QPS to 20.
        rule.setCount(1);
        rules.add(rule);
        //加載配置好的規則
        FlowRuleManager.loadRules(rules);
    }
}      

通路位址:http://localhost:8080/sentinel

1秒鐘之内通路一次正常,如果超過一次将被流控

SpringCloudAlibaba項目之Sentinel流量控制

 4、@SentinelResource 注解方式定義資源

SpringCloudAlibaba項目之Sentinel流量控制

 pom.xml檔案添加依賴

<!-- 使用 @SentinelResource 注解依賴 -->
<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-annotation-aspectj</artifactId>
    <version>1.8.2</version>
</dependency>      

 SentinelAspectConfiguration配置bean

/**
 * 若您的應用使用了 Spring AOP(無論是 Spring Boot 還是傳統 Spring 應用),
 * 您需要通過配置的方式将 SentinelResourceAspect 注冊為一個 Spring Bean:
 */
@Configuration
public class SentinelAspectConfiguration {
    @Bean
    public SentinelResourceAspect sentinelResourceAspect() {
        return new SentinelResourceAspect();
    }
}      
SentinelController:      
/**
 * Sentinel接口流控測試
 */
@RestController
public class SentinelController {
    public static final String USER_RESOURCE_NAME = "user";//資源名
/**
     * 定義規則
     *
     * spring的初始化方法
     */
    @PostConstruct
    private static void initFlowRules(){
        //流控規則
        List<FlowRule> rules = new ArrayList<>();//流控
        FlowRule rule2 = new FlowRule();
        //設定受保護的資源
        rule2.setResource(USER_RESOURCE_NAME);
        // 設定流控規則 QPS
        rule2.setGrade(RuleConstant.FLOW_GRADE_QPS);
        //設定受保護資源的門檻值
        // Set limit QPS to 20.
        rule2.setCount(1);
        rules.add(rule2);

        //加載配置好的規則
        FlowRuleManager.loadRules(rules);
    }

    /**
     * 使用@SentinelResource進行Sentinel流控
     * @SentinelResource注解改善接口鐘資源定義和被流控降級後的處理方法
     * 使用方法:1、添加依賴
     *           2、配置bean-SentinelResourceAspect
     *  value:定義流控資源
     *  blockHandler:設定流控降級後的處理方法(預設該方法必須聲明在同一個類)
     *      如果不想在同一個類中,可以使用 blockHandlerClass 指定,但是方法必須是static
     *  fallback:當接口出現異常,就可以交給fallback指定的方法進行處理
     *      如果不想在同一個類中,可以使用 fallbackClass 指定,但是方法必須是static
     *
     *  注意:如果blockHandler和fallback方法同時指定了,則blockHandler優先級更高
     * @param id
     * @return
     */
    @RequestMapping(value = "/user")
    @SentinelResource(value = USER_RESOURCE_NAME,blockHandler = "blockHandlerForUserTest",fallback = "fallbackForUserTest")
    public User userTest(String id){
        int a = 1/0;
        return new User("張三");
    }

    /**
     * userTest流控降級後的處理方法
     * 注意:
     * 1、一定要是public
     * 2、傳回值一定要和源方法(userTest)保證一緻,包含源方法的參數
     * 3、可以在參數最後添加BlockException,可以區分是什麼規則的處理方法
     * @param id
     * @param ex
     * @return
     */
    public User blockHandlerForUserTest(String id,BlockException ex){
        ex.printStackTrace();
        return new User("流控!");
    }

    /**
     * userTest異常後的處理方法
     * 注意:
     * 1、一定要是public
     * 2、傳回值一定要和源方法(userTest)保證一緻,包含源方法的參數
     * 3、可以在參數最後添加Throwable,可以區分是什麼異常
     * @param id
     * @param e
     * @return
     */
    public User fallbackForUserTest(String id,Throwable e){
        e.printStackTrace();
        return new User("異常處理!");
    }
}      

特别地,若 blockHandler 和 fallback 都進行了配置,則被限流降級而抛出 

BlockException

 時隻會進入 

blockHandler

 處理邏輯。若未配置 

blockHandler

fallback

 和 

defaultFallback

,則被限流降級時會将 

BlockException

 直接抛出(若方法本身未定義 throws BlockException 則會被 JVM 包裝一層 

UndeclaredThrowableException

)。

通路位址:http://localhost:8080/user

SpringCloudAlibaba項目之Sentinel流量控制
SpringCloudAlibaba項目之Sentinel流量控制

 5、服務降級規則體驗

/**
 * Sentinel接口流控測試
 */
@RestController
public class SentinelController {
    public static final String DEGRADE_RESOURCE_NAME = "degrade";//降級資源名
/**
     * 定義服務降級規則
     *
     * spring的初始化方法
     */
    @PostConstruct
    private static void initDegradeRules(){
        //降級規則
        List<DegradeRule> DegradeRules = new ArrayList<>();
        //流控
        DegradeRule degradeRule = new DegradeRule();
        //設定受保護的資源
        degradeRule.setResource(DEGRADE_RESOURCE_NAME);
        // 設定規則測率: 異常數
        degradeRule.setGrade(RuleConstant.DEGRADE_GRADE_EXCEPTION_COUNT);
        //設定異常數
        degradeRule.setCount(2);
        degradeRule.setTimeWindow(10);//10秒内發生的異常
        degradeRule.setMinRequestAmount(2);//最小請求數

        DegradeRules.add(degradeRule);
        //加載配置好的規則
        DegradeRuleManager.loadRules(DegradeRules);
    }

    @RequestMapping("/degrade")
    @SentinelResource(value = DEGRADE_RESOURCE_NAME,entryType = EntryType.IN,blockHandler = "blockHandlerForDegrade")
    public User degrade(String id) throws InternalException {
        // 異常數/比例
        throw new RuntimeException("異常");

        //慢調用比例
        /*TimeUnit.SECONDS.sleep(1);
        return new User("正常");*/
    }
/**
     * degrade服務降級的處理方法
     *
     * @param id
     * @param ex
     * @return
     */
    public User blockHandlerForDegrade(String id,BlockException ex){
        ex.printStackTrace();
        return new User("降級處理!");
    }
}      

通路位址:http://localhost:8080/degrade

SpringCloudAlibaba項目之Sentinel流量控制

 6、控制台部署

控制台文檔:https://github.com/alibaba/Sentinel/wiki/%E6%8E%A7%E5%88%B6%E5%8F%B0

github下載下傳位址(下載下傳對應版本):https://github.com/alibaba/Sentinel/releases

元件版本關系:https://github.com/alibaba/spring-cloud-alibaba/wiki/%E7%89%88%E6%9C%AC%E8%AF%B4%E6%98%8E

從 release 頁面 下載下傳最新版本的控制台 jar 包,使用如下指令啟動控制台:

java -Dserver.port=8080 -Dcsp.sentinel.dashboard.server=localhost:8080 -Dproject.name=sentinel-dashboard -jar sentinel-dashboard.jar

其中 

-Dserver.port=8080

 用于指定 Sentinel 控制台端口為 

8080。

從 Sentinel 1.6.0 起,Sentinel 控制台引入基本的登入功能,預設使用者名和密碼都是 

sentinel

。可以參考 鑒權子產品文檔 配置使用者名和密碼。

注:若您的應用為 Spring Boot 或 Spring Cloud 應用,您可以通過 Spring 配置檔案來指定配置,詳情請參考 Spring Cloud Alibaba Sentinel 文檔。
SpringCloudAlibaba項目之Sentinel流量控制

7、用戶端接入控制台(整合SpringCloud Alibaba)

SpringCloudAlibaba項目之Sentinel流量控制
<!-- sentinel 依賴 -->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>      

application.properties

# 應用名稱
spring.application.name=service-sentinel
# 應用服務 WEB 通路端口
server.port=8081
# Sentinel 控制台位址
spring.cloud.sentinel.transport.dashboard=127.0.0.1:8858
# 取消Sentinel控制台懶加載
# 預設情況下 Sentinel 會在用戶端首次調用的時候進行初始化,開始向控制台發送心跳包
# 配置 sentinel.eager=true 時,取消Sentinel控制台懶加載功能
spring.cloud.sentinel.eager=true
# 如果有多套網絡,又無法正确擷取本機IP,則需要使用下面的參數設定目前機器可被外部通路的IP位址,供admin控制台使用
# spring.cloud.sentinel.transport.client-ip=      
OrderController      
/**
 * 訂單服務
 */
@RestController
@RequestMapping("/order")
public class OrderController {

    /**
     * 新增訂單
     * @return
     */
    @RequestMapping("/addOrder")
    public String addOrder(){
        System.out.println("訂單新增成功");

        return "訂單服務-訂單新增成功";
    }
}      

整合效果:

SpringCloudAlibaba項目之Sentinel流量控制

 8、BlockException統一異常處理

SpringCloudAlibaba項目之Sentinel流量控制

 MyBlockExceptionHandler異常處理類:

注意:想使用BlockException統一異常處理時,不能添加@SentinelResource

/**
 * BlockException統一異常處理
 */
@Component
public class MyBlockExceptionHandler implements BlockExceptionHandler {

    @Override
    public void handle(HttpServletRequest request, HttpServletResponse response, BlockException e) throws Exception {
        System.out.println("資源規則的詳細資訊:" + e.getRule());
        JSONObject resultObj = new JSONObject();
        if(e instanceof FlowException){
            resultObj.put("code",100);
            resultObj.put("msg","接口限流");
        }if(e instanceof DegradeException){
            resultObj.put("code",101);
            resultObj.put("msg","服務降級");
        }if(e instanceof ParamFlowException){
            resultObj.put("code",102);
            resultObj.put("msg","熱點參數限流");
        }if(e instanceof SystemBlockException){
            resultObj.put("code",103);
            resultObj.put("msg","觸發系統保護規則");
        }if(e instanceof AuthorityException){
            resultObj.put("code",104);
            resultObj.put("msg","授權規則不通過");
        }

        //傳回json資料
        response.setStatus(500);
        response.setCharacterEncoding("UTF-8");
        response.setContentType(MediaType.APPLICATION_JSON_VALUE);
        response.getWriter().write(resultObj.toString());
    }
}      

控制台添加流控規則

SpringCloudAlibaba項目之Sentinel流量控制

測試效果:

SpringCloudAlibaba項目之Sentinel流量控制

 9、整合openFeign降級

SpringCloudAlibaba項目之Sentinel流量控制

 pom.xml添加依賴

<!-- nacos 服務注冊發現(用戶端)依賴 -->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
<!-- sentinel 流量控制依賴 -->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<!-- openfeign 遠端調用依賴 -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>      

application.properties添加配置

# openfeign整合sentinel
feign.sentinel.enabled=true      
StockOpenFeignServiceFallback服務降級實作類      
@Component
public class StockOpenFeignServiceFallback implements StockOpenFeignService{

    @Override
    public String subStock(String id) {
        return "subStock-服務降級了!";
    }

    @Override
    public String addStock() {
        return "addStock-服務降級了!";
    }
}      
StockOpenFeignService接口添加服務降級實作類      
/**
 * 庫存服務接口
 * name:指定調用rest接口所對應的服務名
 * path:指定調用rest接口所在的StockController指定的@RequestMapping
 */
@FeignClient(name = "service-stock",path = "stock",fallback = StockOpenFeignServiceFallback.class)
public interface StockOpenFeignService {

    //聲明需要調用的rest接口對應的方法
    /**
     * 庫存扣減
     * @return
     */
    @RequestMapping("/subStock/{id}")
    //@RequestLine("GET /subStock") //feign的原生注解
    String subStock(@PathVariable("id") String id);
    //String subStock(@Param("id") String id); //@PathVariable換成@Param

    /**
     * 庫存新增
     * @return
     */
    @RequestMapping("/addStock")
    //@RequestLine("GET /addStock") //feign的原生注解
    String addStock();
}      

通路位址:http://localhost:8082/order/addOrder

SpringCloudAlibaba項目之Sentinel流量控制

 10、Sentinel規則持久化

  一旦我們重新開機應用,Sentinel規則将消失,生産環境需要将配置規則進行持久化,将限流配置規則持久化進Nacos儲存,隻要重新整理到某個被流控的rest位址,sentinel控制台的流控規則就能看到,隻要Nacos裡面的配置不删除,針對Sentinel上的流控規則持續有效。

<!-- sentinel 規則持久化依賴 -->
<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-datasource-nacos</artifactId>
</dependency>      
spring.application.name=service-sentinel
spring.cloud.nacos.discovery.server-addr= 127.0.0.1:8848
spring.cloud.sentinel.transport.dashboard=localhost:8080
#預設8719,假如被占用了會自動從8719開始依次+1掃描。直至找到未被占用的端口
spring.cloud.sentinel.transport.port= 8719  
spring.cloud.sentinel.datasource.ds1.nacos.server-addr= 127.0.0.1:8848
spring.cloud.sentinel.datasource.ds1.nacos.dataId= service-sentinel
spring.cloud.sentinel.datasource.ds1.nacos.groupId= DEFAULT_GROUP
spring.cloud.sentinel.datasource.ds1.nacos.data-type= json
spring.cloud.sentinel.datasource.ds1.nacos.rule-type= flow      

添加Nacos規則

SpringCloudAlibaba項目之Sentinel流量控制
[
    {
         "resource":"/order/addOrder",
         "limitApp":"default",
         "grade":1,
         "count":1,
         "strategy":0,
         "controlBehavior":0,
         "clusterMode":false
    }
]      

resource: 資源名稱

limitApp: 來源應用

grade: 門檻值類型,0表示線程,1表示QPS

count: 單機門檻值

strategy: 流控模式,0表示直接,1表示關聯,2表示鍊路

controlBehavior: 流控效果,0表示快速失敗,1表示Warm Up,2表示排隊等待

clusterMode: 是否叢集

繼續閱讀