SpringCloud 2020版本教程3:使用sentinel做爲熔斷器

什麼是sentinel

Sentinel,中文翻譯爲哨兵,是爲微服務提供流量控制、熔斷降級的功能,它和Hystrix提供的功能同樣,能夠有效的解決微服務調用產生的「雪崩」效應,爲微服務系統提供了穩定性的解決方案。隨着Hytrxi進入了維護期,再也不提供新功能,Sentinel是一個不錯的替代方案。一般狀況,Hystrix採用線程池對服務的調用進行隔離,Sentinel才用了用戶線程對接口進行隔離,兩者相比,Hystrxi是服務級別的隔離,Sentinel提供了接口級別的隔離,Sentinel隔離級別更加精細,另外Sentinel直接使用用戶線程進行限制,相比Hystrix的線程池隔離,減小了線程切換的開銷。另外Sentinel的DashBoard提供了在線更改限流規則的配置,也更加的優化。html

從官方文檔的介紹,Sentinel 具備如下特徵:java

  • 豐富的應用場景: Sentinel 承接了阿里巴巴近 10 年的雙十一大促流量的核心場景,例如秒殺(即突發流量控制在系統容量能夠承受的範圍)、消息削峯填谷、實時熔斷下游不可用應用等。
  • 完備的實時監控: Sentinel 同時提供實時的監控功能。您能夠在控制檯中看到接入應用的單臺機器秒級數據,甚至 500 臺如下規模的集羣的彙總運行狀況。
  • 普遍的開源生態: Sentinel 提供開箱即用的與其它開源框架/庫的整合模塊,例如與 Spring Cloud、Dubbo、gRPC 的整合。您只須要引入相應的依賴並進行簡單的配置便可快速地接入 Sentinel。
  • 完善的 SPI 擴展點: Sentinel 提供簡單易用、完善的 SPI 擴展點。您能夠經過實現擴展點,快速的定製邏輯。例如定製規則管理、適配數據源等。

下載sentinel dashboard

Sentinel dashboard提供一個輕量級的控制檯,它提供機器發現、單機資源實時監控、集羣資源彙總,以及規則管理的功能。您只須要對應用進行簡單的配置,就可使用這些功能。git

注意: 集羣資源彙總僅支持 500 臺如下的應用集羣,有大概 1 - 2 秒的延時。github

下載最新的sentinel dashboard,下載地址爲github.com/alibaba/Sen…spring

下載完啓動,啓動端口爲8748,啓動命令以下:api

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

複製代碼

改造consumer

本次教程的例子在上一節的例子的基礎上進行改造。瀏覽器

在consumer的pom文件加上spring cloud sentinel的依賴,以下:markdown

<dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
        </dependency>

複製代碼

配置文件application.yml加上以下的配置:app

server:
  port: 8763

spring:
  application:
    name: consumer

  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
    sentinel:
      transport:
        port: 18763
        dashboard: localhost:8748

feign:
  sentinel:
    enabled: true
    
複製代碼

經過feign.sentinel.enable開啓Feign和sentinel的自動適配。配置sentinel的dashboard的地址。框架

經過這樣簡單的配置,Feign和sentinel就配置好了。再啓動三個工程provider\consumer\gateway。

在瀏覽器上屢次訪問http://localhost:5000/consumer/hi-feign

在瀏覽器上訪問localhost:8748,登錄sentinel的控制檯,登錄用戶爲sentinel,密碼爲sentinel。

consumer服務的/hi-feign接口,增長一個流控規則:

qps爲1,快速訪問http://localhost:5000/consumer/hi-feign,會有失敗的狀況。

在Spring cloud gateway上使用sentinel

Spring cloud gateway已經適配了sentinel,在gatewayg工程的pom文件加上相關依賴:

<dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
        </dependency>

        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-alibaba-sentinel-gateway</artifactId>
        </dependency>
複製代碼

在配置文件application.yml上加上sentinel dashboard的配置:

spring:
  cloud:
    sentinel:
     transport:
       port: 15000
       dashboard: localhost:8748

複製代碼

建立一個網關分組和網關的限流規則,代碼以下,參考github.com/alibaba/Sen…

@Configuration
public class GatewayConfiguration {

    private final List<ViewResolver> viewResolvers;
    private final ServerCodecConfigurer serverCodecConfigurer;

    public GatewayConfiguration(ObjectProvider<List<ViewResolver>> viewResolversProvider,
                                ServerCodecConfigurer serverCodecConfigurer) {
        this.viewResolvers = viewResolversProvider.getIfAvailable(Collections::emptyList);
        this.serverCodecConfigurer = serverCodecConfigurer;
    }

    @Bean
    @Order(Ordered.HIGHEST_PRECEDENCE)
    public SentinelGatewayBlockExceptionHandler sentinelGatewayBlockExceptionHandler() {
        // Register the block exception handler for Spring Cloud Gateway.
        return new SentinelGatewayBlockExceptionHandler(viewResolvers, serverCodecConfigurer);
    }

    @Bean
    @Order(-1)
    public GlobalFilter sentinelGatewayFilter() {
        return new SentinelGatewayFilter();
    }

    @PostConstruct
    public void doInit() {
        initCustomizedApis();
        initGatewayRules();
    }

    private void initCustomizedApis() {
        Set<ApiDefinition> definitions = new HashSet<>();
        ApiDefinition api1 = new ApiDefinition("consumer")
                .setPredicateItems(new HashSet<ApiPredicateItem>() {{

                    add(new ApiPathPredicateItem().setPattern("/consumer/**")
                            .setMatchStrategy(SentinelGatewayConstants.URL_MATCH_STRATEGY_PREFIX));
                }});
        ApiDefinition api2 = new ApiDefinition("provider")
                .setPredicateItems(new HashSet<ApiPredicateItem>() {{
                    add(new ApiPathPredicateItem().setPattern("/provider/**")
                            .setMatchStrategy(SentinelGatewayConstants.URL_MATCH_STRATEGY_PREFIX));
                }});
        definitions.add(api1);
        definitions.add(api2);
        GatewayApiDefinitionManager.loadApiDefinitions(definitions);
    }

    private void initGatewayRules() {
        Set<GatewayFlowRule> rules = new HashSet<>();
        rules.add(new GatewayFlowRule("consumer")
                .setCount(10)
                .setIntervalSec(1)
        );
        rules.add(new GatewayFlowRule("consumer")
                .setCount(2)
                .setIntervalSec(2)
                .setBurst(2)
                .setParamItem(new GatewayParamFlowItem()
                        .setParseStrategy(SentinelGatewayConstants.PARAM_PARSE_STRATEGY_CLIENT_IP)
                )
        );
        rules.add(new GatewayFlowRule("provider")
                .setCount(10)
                .setIntervalSec(1)
                .setControlBehavior(RuleConstant.CONTROL_BEHAVIOR_RATE_LIMITER)
                .setMaxQueueingTimeoutMs(600)
                .setParamItem(new GatewayParamFlowItem()
                        .setParseStrategy(SentinelGatewayConstants.PARAM_PARSE_STRATEGY_HEADER)
                        .setFieldName("X-Sentinel-Flag")
                )
        );
        rules.add(new GatewayFlowRule("provider")
                .setCount(1)
                .setIntervalSec(1)
                .setParamItem(new GatewayParamFlowItem()
                        .setParseStrategy(SentinelGatewayConstants.PARAM_PARSE_STRATEGY_URL_PARAM)
                        .setFieldName("pa")
                )
        );
        rules.add(new GatewayFlowRule("provider")
                .setCount(2)
                .setIntervalSec(30)
                .setParamItem(new GatewayParamFlowItem()
                        .setParseStrategy(SentinelGatewayConstants.PARAM_PARSE_STRATEGY_URL_PARAM)
                        .setFieldName("type")
                        .setPattern("warn")
                        .setMatchStrategy(SentinelGatewayConstants.PARAM_MATCH_STRATEGY_CONTAINS)
                )
        );

        rules.add(new GatewayFlowRule("provider")
                .setResourceMode(SentinelGatewayConstants.RESOURCE_MODE_CUSTOM_API_NAME)
                .setCount(5)
                .setIntervalSec(1)
                .setParamItem(new GatewayParamFlowItem()
                        .setParseStrategy(SentinelGatewayConstants.PARAM_PARSE_STRATEGY_URL_PARAM)
                        .setFieldName("pn")
                )
        );
        GatewayRuleManager.loadRules(rules);
    }
}

複製代碼

在sentinel 控制檯爲gateway建立一個限流規則,以下:

流控規則爲qps=1,快速訪問http://localhost:5000/consumer/hi-feign,在qps>1的狀況下,會報如下的錯誤:

Blocked by Sentinel: FlowException
複製代碼

可見sentinel的配置已經生效,更多sentinel教程,請關注fangzhipeng.com網站。

參考文檔

www.fangzhipeng.com/springcloud…

github.com/alibaba/Sen…

github.com/alibaba/Sen…

github.com/spring-clou…

github.com/alibaba/Sen…

源碼下載

github.com/forezp/Spri…

相關文章
相關標籤/搜索