學習 spring-cloud-aibaba第五篇,服務容錯 Sentinel 下篇


特別聲明:整理自慕課網大目師兄的微服務視頻,鏈接:https://coding.imooc.com/learn/list/358.html
前情提要:接續服務容錯 Sentinel 上篇

7.熱點參數規則限流

熱點即經常訪問的數據
商品 ID 爲參數,統計一段時間內最常購買的商品 ID 並進行限制
用戶 ID 爲參數,針對一段時間內頻繁訪問的用戶 ID 進行限制
在這裏插入圖片描述

7.1 針對第二個傳參限流

規則:如果第二個參數有值,QPS達到閾值,限流;如果第二個參數沒傳值,不限流

  • 準備代碼
    @SentinelResource註解的作用是把參數納入統計
@RestController
@Slf4j
@RequestMapping("/hot")
public class HotParamController {

    @GetMapping("basic")
    // 把方法的參數納入統計
    @SentinelResource("basic")
    public String basic(String a,String b){
        log.info("basic......");
        return a + ":" + b;
    }

7.2 針對參數具體值的限流

在這裏插入圖片描述
熱點規則 ------> 點擊編輯(新增的時候是沒有的)

8.系統規則限流

  • 應用級別的入口流量進行控制,從單臺機器的總體 Load、RT、入口 QPS 和線程數四個維度監控應用數據,讓系統儘可能跑在最大吞吐量的同時保證系統整體的穩定性
  • 系統保護規則是應用整體維度的,而不是資源維度的,並且僅對入口流量生效
    在這裏插入圖片描述
    在這裏插入圖片描述

8.1 四種閾值類型

8.1.1 Load

8.1.1.1 釋義

  • 當系統load1(一分鐘的負載)超過閾值,且系統當前的併發線程數超過系統容量時纔會觸發系統保護
  • 系統容量:系統的 maxQps * minRt 計算得出
    maxQps:秒級統計最大的QP
    minRt:秒級統計的最小響應時間
  • 閾值:一般是CPU cores * 2.5
  • 僅對Linux/Unix-like機器生效

8.1.1.2 查看linux系統的load

uptime

在這裏插入圖片描述
紅框裏的三個數分別是1分鐘內的平均負載,5分鐘內的平均負載,15分鐘內的平均負載

8.1.2 RT

所有入口流量的平均RT

8.1.3 線程數

所有入口流量的併發線程數

8.1.4 QPS

所有入口流量的QPS

9.授權規則限流

針對應用級別的黑白名單,參數的意思也都很好理解
在這裏插入圖片描述

10.sentinel控制檯如何監控微服務數據?

11.常用的自定義參數

11.1 服務方常用的自定義參數

在這裏插入圖片描述

11.2 sentinel控制檯常用的自定義參數

在這裏插入圖片描述
在這裏插入圖片描述

  • 自定義端口示例
java -jar -DServer.port=8090 sentinel-dashboard-1.6.3.jar

在這裏插入圖片描述
端口已改成8090
在這裏插入圖片描述

12.Feign,RestTemplate整合Sentinel

Sentinel不僅可以控制本服務的資源,還可以限流Feign和RestTemplate對外的請求

12.1 RestTemplate整合Sentinel

  • 添加註解@SentinelRestTemplate
@Configuration
public class RestTemplateConfig {

    @Bean
    @LoadBalanced
    @SentinelRestTemplate
    public RestTemplate getRestTemplate(){
        return new RestTemplate();
    }
}
  • sentinel控制檯添加限流規則,簇點鏈路這邊可以看到restTemplate發送的請求了,添加註解之前是沒有的
    在這裏插入圖片描述
  • 關閉註解@SentinelRestTemplate,默認是開啓的
resttemplate:
  sentinel:
    # 關閉 @SentinelRestTemplate 作用
    enabled: false

12.2 Feign整合Sentinel

  • 寫配置
feign:
  sentinel:
    # IDE不會提示,但是有效
    enabled: true
  • 使用feign,sentinel這邊已經可以看到 GET:http://user-center/reciteHis/testAno 資源了
    在這裏插入圖片描述
    如果spring-cloud-alibaba是0.9.0版本並且你的Feign是用繼承模式實現的,那麼會報 NullPointerException 錯誤,把spring-cloud-alibaba升級到2.1.0.RELEASE就沒有了,我踩了這個坑…

13.Sentinel 擴展

13.1 Sentinel規則錯誤提示優化

之前,我測試的時候,限流規則,降級規則,熱點規則等待,返回的都是一句錯誤消息,或者乾脆報異常,無法區別到底違反了哪個規則?這一節的擴展可以優化這個問題

13.1.1 普通spring mvc資源的錯誤提示優化

  • ContentUrlBlockHandler 實現 UrlBlockHandler,並加上@Component
package com.zengchen.content.sentinel;

import com.alibaba.csp.sentinel.adapter.servlet.callback.UrlBlockHandler;
import com.alibaba.csp.sentinel.slots.block.BlockException;
import com.alibaba.csp.sentinel.slots.block.authority.AuthorityException;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeException;
import com.alibaba.csp.sentinel.slots.block.flow.FlowException;
import com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowException;
import com.alibaba.csp.sentinel.slots.system.SystemBlockException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.zengchen.content.common.ResponseVO;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@Component
public class ContentUrlBlockHandler implements UrlBlockHandler {
    @Override
    public void blocked(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, BlockException e) throws IOException {
        ResponseVO sentinelErrorMsg = null;
        // 根據異常類型區分違反哪種規則
        if(e instanceof FlowException){// 限流異常
            sentinelErrorMsg = ResponseVO.builder()
                    .code(100)
                    .msg("被限流規則阻擋")
                    .build();
        }else if(e instanceof DegradeException){ // 降級異常
            sentinelErrorMsg = ResponseVO.builder()
                    .code(101)
                    .msg("被降級規則阻擋")
                    .build();
        }else if(e instanceof ParamFlowException){ // 熱點參數異常
            sentinelErrorMsg = ResponseVO.builder()
                    .code(102)
                    .msg("被熱點參數規則阻擋")
                    .build();
        }else if(e instanceof SystemBlockException){ // 系統規則異常
            sentinelErrorMsg = ResponseVO.builder()
                    .code(103)
                    .msg("被系統規則阻擋")
                    .build();
        }else if(e instanceof AuthorityException){ // 認證異常
            sentinelErrorMsg = ResponseVO.builder()
                    .code(104)
                    .msg("被授權規則阻擋")
                    .build();
        }else{
            sentinelErrorMsg = ResponseVO.builder()
                    .code(999)
                    .msg("Unknown")
                    .build();
        }
        httpServletResponse.setStatus(500);
        httpServletResponse.setCharacterEncoding("utf-8");
        httpServletResponse.setHeader("Content-Type","application/json;charset=utf-8");
        httpServletResponse.setContentType("application/json;charset=utf-8");
        new ObjectMapper().writeValue(httpServletResponse.getWriter(),sentinelErrorMsg);
    }
}

ResponseVO

package com.zengchen.content.common;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * http請求返回的最外層對象
 */
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ResponseVO<T> {

    /**
     * 錯誤碼
     */
    private Integer code;

    /**
     * 提示信息
     */
    private String msg;

    /**
     * 具體內容
     */
    private T data;

    public static ResponseVO success(Object data){
        ResponseVO responseVO = new ResponseVO();
        responseVO.setCode(0);
        responseVO.setData(data);
        responseVO.setMsg("OK");
        return responseVO;
    }

    public static ResponseVO fail(Integer code,String msg){
        ResponseVO responseVO = new ResponseVO();
        responseVO.setCode(code);
        responseVO.setMsg(msg);
        return responseVO;
    }

}

  • 測試
    限流規則
    在這裏插入圖片描述
    降級規則
    在這裏插入圖片描述
    熱點參數規則
    失敗了,程序並沒有走進ContentUrlBlockHandler,因爲熱點參數測試方法上添加了@SentinelResource註解,有這個註解就不能使用ContentUrlBlockHandler來做錯誤頁優化,下小節會講


    系統規則
    在這裏插入圖片描述
    授權規則
    在這裏插入圖片描述

13.1.2 使用@SentinelResource註解資源的錯誤提示優化

主要使用@SentinelResource註解的blockHandlerblockHandlerClassfallbackfallbackClass四個屬性

13.1.2.1 屬性詳解

  • blockHandler
    值是方法名,資源受到規則限制,會調用此方法
    方法要求:訪問控制 public,返回值與資源方法一直,參數是資源方法參數 + BlockException e
  • blockHandlerClass
    blockHandler方法所在位置於資源方法不在一個類時,用這個屬性指定
  • fallback
    值是方法名,資源方法發生異常時,會調用此方法
    方法要求:訪問控制 public,返回值與資源方法一直,參數是資源方法參數 + Throwable e(版本1.6.0之和纔有Throwable參數)
  • fallbackClass
    fallback方法所在位置於資源方法不在一個類時,用這個屬性指定

13.1.2.2 示例代碼

  • 資源方法:
@RestController
@Slf4j
public class SentinelResourceController {

    @GetMapping("/test-blockHandler")
    @SentinelResource(value = "blockHandler",
            blockHandler = "block",blockHandlerClass = SentinelResourceBlockHandler.class ,
            fallback = "fallBack",fallbackClass = SentinelResourceFallbackHandler.class)
    public String testBlockHandler(String a,String b) throws InterruptedException {
        throw new IllegalArgumentException();
    }

}
  • SentinelResourceBlockHandler
@Slf4j
public class SentinelResourceBlockHandler {

    public static  String block(String a,String b, BlockException e){
        String errorMsg = "Unknown";
        // 根據異常類型區分違反哪種規則
        if(e instanceof FlowException){// 限流異常
            errorMsg = "被限流規則阻擋";
        }else if(e instanceof DegradeException){ // 降級異常
            errorMsg = "被降級規則阻擋";
        }else if(e instanceof ParamFlowException){ // 熱點參數異常
            errorMsg = "被熱點參數規則阻擋";
        }else if(e instanceof SystemBlockException){ // 系統規則異常
            errorMsg = "被系統規則阻擋";
        }else if(e instanceof AuthorityException){ // 認證異常
            errorMsg = "被授權規則阻擋";
        }
        log.warn("block method,{}",errorMsg ,e);
        return errorMsg;
    }


}
  • SentinelResourceFallbackHandler
@Slf4j
public class SentinelResourceFallbackHandler {

    public static String fallBack(String a,String b,Throwable e){
        log.warn("fallBack method",e);
        return "出現異常了";
    }
}

13.1.3 Feign 通訊資源的錯誤提示優化

還記得@FeignClient註解嗎?也有fallback,fallbackFactory屬性
只能配置一個總的,因爲都是一個feignClient,入口統一了

  • 代碼
    SentinelFeignFallbackFactory
@Component
@Slf4j
public class SentinelFeignFallbackFactory implements FallbackFactory<UserCenterFeignClient> {

    @Override
    public UserCenterFeignClient create(Throwable throwable) {
        return new UserCenterFeignClient() {
            @Override
            public Page<ReciteHisOT> memberRctHisAno() {
                log.warn("memberRctHisAno FallbackFactory");
                return null;
            }
        };
    }
}

UserCenterFeignClientfallbackFactory

@FeignClient(name = "user-center", fallbackFactory = SentinelFeignFallbackFactory.class)
public interface UserCenterFeignClient
        extends UserCenterFeignClientService
{

    /**
     * FeignClient的name + GetMapping的value
     * 相當於 http://user-center/reciteHis/testAno
     * 和RestTemplate裏寫的url一模一樣
     * @return Page
     */
//    @GetMapping(value = "/reciteHis/testAno")
//    Page<ReciteHisOT> memberRctHisAno();
}

配置個限流規則,觸發規則時,進入fallbackFactory
在這裏插入圖片描述

13.1.4 RestTemplate 通訊資源的錯誤提示優化

還記得RestTemplate怎麼和sentinel整合的嗎?也是一個註解@SentinelRestTemplate,這個註解和@SentinelResource一樣,有blockHandlerblockHandlerClassfallbackfallbackClass四個屬性
也只能配置一個總的,因爲都是一個入口
不寫代碼了,反正我用Feign

13.2 Sentinel 實現區分來源

授權規則的黑白名單需要來源,流控規則也有針對來源的屬性,但是這個來源是怎麼來的呢?sentinel提供了RequestOriginParser接口,寫個類實現這個接口,唯一方法的返回值就是來源

  • 代碼
@Component
public class ContentRequestOriginParser implements RequestOriginParser {
    @Override
    public String parseOrigin(HttpServletRequest httpServletRequest) {
//        String origin = httpServletRequest.getHeader("origin");
        String origin = httpServletRequest.getParameter("origin"); // 放在參數中方便測試
        if(StringUtils.isBlank(origin)){
            throw new IllegalArgumentException("origin is blank!");
        }
        return origin;
    }
}
  • 測試授權規則
    在這裏插入圖片描述
    在這裏插入圖片描述

13.3 Sentinel 對Restful Url的支持

問題說明:如下圖,僅僅id不一樣,這規則怎麼配置?
在這裏插入圖片描述
sentinelUrlCleaner會幫助我們解決這個問題

  • restful接口DownController
    @GetMapping("restful/{id}")
    public String restful(@PathVariable Integer id) {
        log.info("restful start with {}", id);
        return "" + id;
    }

    @GetMapping("restful/{id}/test")
    public String restfulTest(@PathVariable Integer id) {
        log.info("restfulTest start with {}", id);
        return "test " + id;
    }
  • UrlCleaner
@Component
@Slf4j
public class ContentUrlCleaner implements UrlCleaner {
    @Override
    public String clean(String path) {
        log.info("ContentUrlCleaner start with {}", path);
        String[] paths = path.split("/");
        path = Arrays.stream(paths).map(string -> {
            if (NumberUtils.isNumber(string)) {
                return "{number}";
            }
            return string;
        }).reduce((a, b) -> {
            return a + "/" + b;
        }).orElse("");
        log.info("ContentUrlCleaner end with {}", path);
        return path;
    }
}
  • sentinel控制檯,簇點鏈路,url已經改變
    在這裏插入圖片描述

14.Sentinel 規則持久化

現在sentinel有個致命缺陷,服務重啓或者sentinel重啓,配置的規則都會丟失,如果是生產上,這還了得?這種事絕對不能發生。下面介紹三種解決辦法

14.1 拉模式

參考大目老師手記:https://www.imooc.com/article/289402,生產環境不建議用這個,因爲使用之後後患不小

14.2 推模式

14.3 阿里 ahas

免費的可以接入5個節點,專業的收費
開通地址:https://ahas.console.aliyun.com
開通說明:https://help.aliyun.com/document_detail/90323.html
在這裏插入圖片描述
在這裏插入圖片描述

14.3.1 加依賴

<dependency>
   <groupId>com.alibaba.csp</groupId>
   <artifactId>spring-boot-starter-ahas-sentinel-client</artifactId>
   <version>1.3.5</version>
</dependency>

在這裏插入圖片描述

14.3.2 添加埋點

springcloud-alibaba已經幫我們做過了,省略

14.3.3 配置啓動參數

這裏選擇公網,纔有license
在這裏插入圖片描述
我的ahas.license就不暴露出來了
在這裏插入圖片描述
註釋掉

#      transport:
#        # 指定 sentinel 控制檯地址
#        dashboard: localhost:8080

14.3.4 重啓應用

隨便訪問一個接口,比如:http://localhost:8081/down/restful/2/test,再刷新阿里的控制檯,就可以看到了
在這裏插入圖片描述
在這裏插入圖片描述
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章