Sentinel筆記

1.常見的處理服務雪崩的方式

  • 超時時間
  • 限流
  • 倉壁模式
  • 斷路器模式

2.Sentinel流控方式

流控模式:

  • 直接:根據(jù)QPS或者線程數(shù)直接關聯(lián),超過閾值直接失敗
  • 關聯(lián):關聯(lián)資源超過閾值,則失敗
  • 鏈路:綁定某入口資源的閾值,超時則失敗

流控策略:

  • 快速失?。褐苯邮伋霎惓?,源碼位置com.alibaba.csp.sentinel.slots.block.flow.controller.DefaultController
  • Warm Up : 預熱模式,設置初始流量為閾值/codeFactor(默認為3),經(jīng)過設置的默認時長才達到閾值,源碼位置:com.alibaba.csp.sentinel.slots.block.flow.controller.WarmUpController,適用于大流量,避免一次性流量過大導致服務直接崩潰。
  • 排隊等待:請求勻速通過,閾值必須設置成QPS,否則無效。適用于流量激增。

3.降級策略

  • RT(秒級統(tǒng)計),在時間窗口內QPS>=5次,并且平均響應時間超出閾值,則觸發(fā)降級,在時間窗口結束后關閉降級,RT的最大響應設置時間為4900ms,若需要指定更大,則需要通過-Dcsp.sentinel.statistic.max.rt=xx
  • 異常比例(秒級統(tǒng)計),QPS>=5并且異常比例超過閾值,則會進入熔斷
  • 異常數(shù)(分鐘統(tǒng)計),異常數(shù)超過閾值,則觸發(fā)降級,若時間窗設置小于60S,那么可能在降級時間窗結束后在此進入降級

4.熱點策略

  • 針對api接口參數(shù)特定的值進行限流,其中配置參數(shù)索引的特點熱點值數(shù)據(jù)只對基本數(shù)據(jù)類型和String有效
  • 可參考相關源碼com.alibaba.csp.sentinel.slots.block.flow.param.ParamFlowChecker#passCheck

[圖片上傳失敗...(image-87caf8-1565855752793)]

5.系統(tǒng)規(guī)則(load1,cpu,..)

load1 = cpu核心數(shù) * 2.5

6.授權規(guī)則

針對微服務名稱進行黑白名單設置,可以通過定義RequestOriginParser來設置名稱規(guī)則

7.代碼配置方式配置Sentinel容錯策略

ex : 初始化一個簡單的流控規(guī)則,在資源執(zhí)行前調用即可

    private void initFlowQpsRule() {
        List<FlowRule> ruleList = new ArrayList<>();
        FlowRule rule = new FlowRule("/c");
        //設置閾值
        rule.setCount(1);
        //設置流控閾值類型
        rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
        //設置流控模式
        rule.setStrategy(RuleConstant.STRATEGY_DIRECT);
        //設置流控策略
        rule.setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_DEFAULT);
        rule.setLimitApp("default");
        ruleList.add(rule);
        FlowRuleManager.loadRules(ruleList);
    }

詳細所有的配置信息參考 - > 大佬Sentinel博客

8.Sentinel Dashboard 是如何知道微服務的信息?如何通信?

  • Sentinel實現(xiàn)了服務注冊和發(fā)現(xiàn)
  • 通過配置的通信端口+服務ip,sentinel可以使用http調用其api來操作設置容錯規(guī)則和獲取監(jiān)控信息

源碼解析:

注冊/心跳發(fā)送:com.alibaba.csp.sentinel.transport.heardheat.SimpleHttpHeartbeatSender

通信api : com.alibaba.csp.sentinel.commonand.CommandHandler相關實現(xiàn)類

9.Sentinel Api的簡單使用

  • ContextUtil 定義入口資源
  • Sphu 定義資源
  • Tracer.trace() 統(tǒng)計資源 , 在非BlockException的異常時候需要顯示的使用來統(tǒng)計,不然不會自動統(tǒng)計
   @GetMapping("/test-sentinel-api")
    public String testSentinelApi() {
       String resourceName = "test-sentinel-api";
        ContextUtil.enter(resourceName, "test-micro-service");
        Entry entry = null;
        try {
            //定義一個資源
            entry = SphU.entry(resourceName);
            //執(zhí)行業(yè)務邏輯
            if (!StringUtils.isNotBlank("")) {
                throw new IllegalAccessException("參數(shù)非法");
            }
            return "success";
        } catch (BlockException e) {
            return "容錯觸發(fā)?。?;
        } catch (IllegalAccessException e) {
            //默認除了BlockException及其子類會計算容錯次數(shù),異常數(shù)等等。。
            //其他異常需要使用Tracer.trace()進行統(tǒng)計
            Tracer.trace(e);
            return "非法參數(shù)";
        } finally {
            if (entry != null) {
                entry.exit();
            }
            ContextUtil.exit();
        }
    }

10.sentinel整合RestTemplate和feign

  • 直接在注入的RestTemplate上面加入@SentinelRestTemplate注解即可

核心處理原理見 : SentinelBeanPostProcessor

  • 整合feign,直接在配置文件中指定feing.sentinel.enabled=true即可。

11.持久化Sentinel規(guī)則

  • 默認不持久化,保存在內存中
  • pull模式,將sentinel規(guī)則保存在文件或者數(shù)據(jù)庫中
  • push模式(生成環(huán)境使用),將sentinel的持久化規(guī)則同步到nacos/zk/Apollo等配置中心上

push模式的集成

改造sentinel-dashboard控制臺

因為push模式是sentinel-dashboard直接和遠程配置中心直接交互,而不用sentinel客戶端去參加

修改步驟:

  • 將sentinel-datasource-nacos依賴的范圍變成compile
        <!-- for Nacos rule publisher sample -->
        <dependency>
            <groupId>com.alibaba.csp</groupId>
            <artifactId>sentinel-datasource-nacos</artifactId>
<!--            <scope>test</scope>-->
        </dependency>
  • 修改拉取和推送的策略

具體實現(xiàn)見 github

微服務集成sentinel持久化規(guī)則
  • 加依賴
        <dependency>
            <groupId>com.alibaba.csp</groupId>
            <artifactId>sentinel-datasource-nacos</artifactId>
        </dependency>
  • 配置
spring:
  cloud:
    sentinel:
      transport:
        dashboard: localhost:8080
        port: 8820
      datasource:
        # 名稱隨意
        flow:
          nacos:
            server-addr: 192.168.18.91:8848
            dataId: ${spring.application.name}-flow-rules
            groupId: SENTINEL_GROUP
            # 規(guī)則類型,取值見:
            # org.springframework.cloud.alibaba.sentinel.datasource.RuleType
            rule-type: flow
        degrade:
          nacos:
            server-addr: 192.168.18.91:8848
            dataId: ${spring.application.name}-degrade-rules
            groupId: SENTINEL_GROUP
            rule-type: degrade
        param-flow:
          nacos:
            server-addr: 192.168.18.91:8848
            dataId: ${spring.application.name}-param-flow-rules
            groupId: SENTINEL_GROUP
            rule-type: param-flow

12.sentinel在默認是會攔截所有的controller請求

可配置排除

spring:
    cloud:
        sentinel:
            filter:
              enabled: false

如何定制默認的controller請求錯誤攔截頁面?實現(xiàn)UrlBlockHandler即可

@Component
public class MyUrlBlockHandler implements UrlBlockHandler {
    @Override
    public void blocked(HttpServletRequest httpServletRequest, HttpServletResponse response, BlockException e) throws IOException {
        ErrorMsg msg = null;
      //根據(jù)異常類型的不同判斷是發(fā)生了限流還是降級
        if (e instanceof FlowException) {
            msg = ErrorMsg.builder()
                    .msg("被限流了")
                    .status(101)
                    .build();
        } else if (e instanceof DegradeException) {
            msg = ErrorMsg.builder()
                    .msg("被降級了")
                    .status(101)
                    .build();
        }
        response.setContentType("application/json;charset=utf-8");
        response.setCharacterEncoding("utf-8");
        response.setStatus(500);
        new ObjectMapper()
                .writeValue(
                        response.getWriter(),
                        msg
                );
    }

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    @Builder
    private static class ErrorMsg {
        private String msg;
        private Integer status;
    }
}

13.Sentinel的來源應用配置

  • 可以通過不同的來源來配置匹配的策略。
  • 可以通過授權規(guī)則來添加黑白名單。

通過實現(xiàn)RequestOriginParser來實現(xiàn),該方法的返回值就是應用的名稱。

/**
 * @author hj
 * 2019-08-15 13:55
 * 自定義區(qū)分來源解釋器,通過origin請求參數(shù)來區(qū)分來源應用
 */
@Component
public class MyRequestOriginParser implements RequestOriginParser {
    @Override
    public String parseOrigin(HttpServletRequest httpServletRequest) {
        //如果參數(shù)中有origin參數(shù)那么獲取并獲取值作為來源
        String origin = httpServletRequest.getParameter("origin");
        if (StringUtils.isBlank(origin)) {
            throw new IllegalArgumentException("origin must not be null");
        }
        //無則拋出異常
        return origin;
    }
}

14.sentinel對Restful-url的支持

sentinel默認對/a/{id}這種格式url會隨著參數(shù)變化而產生多個資源,那么如何讓這些資源共享一個sentinel規(guī)則? 通過UrlCleaner實現(xiàn)類將這種rest風格轉化成相同的url

實現(xiàn)UrlCleaner接口

/**
 * @author hj
 * 2019-08-15 14:24
 * 擴展restFul-Url,讓/shares/* 使用相同的邏輯
 */
@Component
public class MyUrlCleaner implements UrlCleaner {
    @Override
    public String clean(String url) {
        String[] split = url.split("/");
        return Arrays.stream(split)
                .map(a -> {
                    if (NumberUtils.isNumber(a)) {
                        return "{number}";
                    }
                    return a;
                })
                .reduce((a, b) -> a + "/" + b)
                .orElse("");
    }
}

15總結擴展表格

其實上面的擴展點都是來源于CommonFilter

public class CommonFilter implements Filter {

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
        throws IOException, ServletException {
        HttpServletRequest sRequest = (HttpServletRequest)request;
        Entry urlEntry = null;

        try {
            String target = FilterUtil.filterTarget(sRequest);
            //處理 REST APIS 的UrlCleaner
            UrlCleaner urlCleaner = WebCallbackManager.getUrlCleaner();
            if (urlCleaner != null) {
                target = urlCleaner.clean(target);
            }

            if (!StringUtil.isEmpty(target)) {
              //處理來源的RequestOriginParser
                String origin = parseOrigin(sRequest);
                ContextUtil.enter(WebServletConfig.WEB_SERVLET_CONTEXT_NAME, origin);

                if (httpMethodSpecify) {
                    // Add HTTP method prefix if necessary.
                    String pathWithHttpMethod = sRequest.getMethod().toUpperCase() + COLON + target;
                    urlEntry = SphU.entry(pathWithHttpMethod, ResourceTypeConstants.COMMON_WEB, EntryType.IN);
                } else {
                    urlEntry = SphU.entry(target, ResourceTypeConstants.COMMON_WEB, EntryType.IN);
                }
            }
            chain.doFilter(request, response);
        } catch (BlockException e) {
            HttpServletResponse sResponse = (HttpServletResponse)response;
            //處理錯誤頁面返回的UrlBlockHandler
            WebCallbackManager.getUrlBlockHandler().blocked(sRequest, sResponse, e);
        } catch (IOException | ServletException | RuntimeException e2) {
            Tracer.traceEntry(e2, urlEntry);
            throw e2;
        } finally {
            if (urlEntry != null) {
                urlEntry.exit();
            }
            ContextUtil.exit();
        }
    
核心接口 處理問題
UrlBlockHandler 錯誤處理
UrlCleaner 合并rest apis
RequestOriginParser 來源控制ContextUtil

更多配置和詳細,見Sentinel GitHub

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

相關閱讀更多精彩內容

友情鏈接更多精彩內容