SpringCloudGateway集成Sentinel

介紹

Spring Cloud Gateway是Spring Cloud官方推出的第二代網(wǎng)關(guān)框架编曼,取代Zuul網(wǎng)關(guān)揉忘。網(wǎng)關(guān)作為流量的矿筝,在微服務(wù)系統(tǒng)中有著非常作用兜材,網(wǎng)關(guān)常見(jiàn)的功能有路由轉(zhuǎn)發(fā)罕邀、權(quán)限校驗(yàn)乏苦、限流控制等作用株扛。
Sentinel是阿里開(kāi)源的項(xiàng)目,提供了流量控制汇荐、熔斷降級(jí)洞就、系統(tǒng)負(fù)載保護(hù)等多個(gè)維度來(lái)保障服務(wù)之間的穩(wěn)定性。(https://github.com/alibaba/Sentinel)
  • 整體結(jié)構(gòu)圖如下掀淘,將原有的 Spring Cloud Gateway中集成Hystrix替換成Sentinel來(lái)進(jìn)行限流旬蟋、降級(jí)等功能, Hystrix和Sentinel的區(qū)別可以參考:[Hystrix和Sentinel對(duì)比][1]革娄,總結(jié)來(lái)說(shuō):Hystrix常用的線(xiàn)程池隔離會(huì)造成線(xiàn)程上下切換的overhead比較大倾贰;Hystrix使用的信號(hào)量隔離對(duì)某個(gè)資源調(diào)用的并發(fā)數(shù)進(jìn)行控制,效果不錯(cuò)拦惋,但是無(wú)法對(duì)慢調(diào)用進(jìn)行自動(dòng)降級(jí)匆浙;Sentinel通過(guò)并發(fā)線(xiàn)程數(shù)的流量控制提供信號(hào)量隔離的功能
    1.png

Gateway網(wǎng)關(guān)接入

  • 創(chuàng)建項(xiàng)目mas-openapi-geteway,遵從SpringBoot家族開(kāi)箱即用的慣例厕妖,在maven中加入如下配置:
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
            <version>2.1.2.RELEASE</version>
        </dependency>
  • gateWay內(nèi)部實(shí)際使用的是Reactor模式首尼,所有的請(qǐng)求都是異步非阻塞處理的,加入webFlex的配置:
       <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-webflux</artifactId>
            <version>2.1.7.RELEASE</version>
        </dependency>
  • gateWay的主要功能之一是轉(zhuǎn)發(fā)請(qǐng)求言秸,轉(zhuǎn)發(fā)規(guī)則的定義主要包含三個(gè)部分软能,其中Route和Predicate必須同時(shí)申明:
Route(路由) 路由是網(wǎng)關(guān)的基本單元,由ID举畸、URI查排、一組Predicate、一組Filter組成抄沮,根據(jù)Predicate進(jìn)行匹配轉(zhuǎn)發(fā)跋核。
Predicate(謂語(yǔ)岖瑰、斷言) 路由轉(zhuǎn)發(fā)的判斷條件,目前SpringCloud Gateway支持多種方式了罪,常見(jiàn)如:Path锭环、Query、Method泊藕、Header等辅辩,寫(xiě)法必須遵循 key=vlue的形式
Filter(過(guò)濾器) 過(guò)濾器是路由轉(zhuǎn)發(fā)請(qǐng)求時(shí)所經(jīng)過(guò)的過(guò)濾邏輯,可用于修改請(qǐng)求娃圆、響應(yīng)內(nèi)容
  • 規(guī)則可通過(guò)yml文件方式玫锋、代碼方式和動(dòng)態(tài)推送(通過(guò)配置中心Nacos推送)配置,這里網(wǎng)關(guān)的地址為localhost:9022
//通過(guò)配置文件配置
spring:
  cloud:
    gateway:
      routes:
        - id: gate_route
          uri: http://localhost:9023
          predicates:
          ## 當(dāng)請(qǐng)求的路徑為gate讼呢、rule開(kāi)頭的時(shí)撩鹿,轉(zhuǎn)發(fā)到http://localhost:9023服務(wù)器上
            - Path=/gate/**,/rule/**
        ### 請(qǐng)求路徑前加上/app
          filters:
          - PrefixPath=/app
  • 轉(zhuǎn)發(fā)規(guī)則(predicates),轉(zhuǎn)發(fā)uri都設(shè)定為http://localhost:9023
規(guī)則 實(shí)例 說(shuō)明
Path - Path=/gate/,/rule/ ## 當(dāng)請(qǐng)求的路徑為gate悦屏、rule開(kāi)頭的時(shí)节沦,轉(zhuǎn)發(fā)到http://localhost:9023服務(wù)器上
Before - Before=2017-01-20T17:42:47.789-07:00[America/Denver] 在某個(gè)時(shí)間之前的請(qǐng)求才會(huì)被轉(zhuǎn)發(fā)到 http://localhost:9023服務(wù)器上
After - After=2017-01-20T17:42:47.789-07:00[America/Denver] 在某個(gè)時(shí)間之后的請(qǐng)求才會(huì)被轉(zhuǎn)發(fā)
Between - Between=2017-01-20T17:42:47.789-07:00[America/Denver],2017-01-21T17:42:47.789-07:00[America/Denver] 在某個(gè)時(shí)間段之間的才會(huì)被轉(zhuǎn)發(fā)
Cookie - Cookie=chocolate, ch.p 名為chocolate的表單或者滿(mǎn)足正則ch.p的表單才會(huì)被匹配到進(jìn)行請(qǐng)求轉(zhuǎn)發(fā)
Header - Header=X-Request-Id, \d+ 攜帶參數(shù)X-Request-Id或者滿(mǎn)足\d+的請(qǐng)求頭才會(huì)匹配
Host - Host=www.hd123.com 當(dāng)主機(jī)名為www.hd123.com的時(shí)候直接轉(zhuǎn)發(fā)到http://localhost:9023服務(wù)器上
Method - Method=GET 只有GET方法才會(huì)匹配轉(zhuǎn)發(fā)請(qǐng)求,還可以限定POST础爬、PUT等請(qǐng)求方式
  • 過(guò)濾器規(guī)則(Filter)
過(guò)濾規(guī)則 實(shí)例 說(shuō)明
PrefixPath - PrefixPath=/app 在請(qǐng)求路徑前加上app
RewritePath - RewritePath=/test, /app/test 訪(fǎng)問(wèn)localhost:9022/test,請(qǐng)求會(huì)轉(zhuǎn)發(fā)到localhost:8001/app/test
SetPath SetPath=/app/{path} 通過(guò)模板設(shè)置路徑甫贯,轉(zhuǎn)發(fā)的規(guī)則時(shí)會(huì)在路徑前增加app,{path}表示原請(qǐng)求路徑

注:當(dāng)配置多個(gè)filter時(shí)看蚜,優(yōu)先定義的會(huì)被調(diào)用叫搁,剩余的filter將不會(huì)生效

  • 通過(guò)代碼進(jìn)行配置,將路由規(guī)則設(shè)置為一個(gè)Bean即可:
    @Bean
    public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
        return builder.routes()
            .route("path_route", r -> r.path("/get")
                .uri("http://httpbin.org"))
            .route("host_route", r -> r.host("*.myhost.org")
                .uri("http://httpbin.org"))
            .route("rewrite_route", r -> r.host("*.rewrite.org")
                .filters(f -> f.rewritePath("/foo/(?<segment>.*)", "/${segment}"))
                .uri("http://httpbin.org"))
            .route("hystrix_route", r -> r.host("*.hystrix.org")
                .filters(f -> f.hystrix(c -> c.setName("slowcmd")))
                .uri("http://httpbin.org"))
            .route("hystrix_fallback_route", r -> r.host("*.hystrixfallback.org")
                .filters(f -> f.hystrix(c -> c.setName("slowcmd").setFallbackUri("forward:/hystrixfallback")))
                .uri("http://httpbin.org"))
            .route("limit_route", r -> r
                .host("*.limited.org").and().path("/anything/**")
                .filters(f -> f.requestRateLimiter(c -> c.setRateLimiter(redisRateLimiter())))
                .uri("http://httpbin.org"))
            .build();
    }
  • 使用nacos實(shí)現(xiàn)動(dòng)態(tài)路由供炎,以上兩種方式都是實(shí)現(xiàn)的靜態(tài)配置路徑渴逻,只能應(yīng)對(duì)部分場(chǎng)景,接下來(lái)配置nacos實(shí)現(xiàn)動(dòng)態(tài)配置以及配置的存儲(chǔ)音诫,由于gateWay并沒(méi)有適配nacos惨奕,需要自定義監(jiān)聽(tīng)器:
@Component
@Slf4j
public class NacosDynamicRouteService implements ApplicationEventPublisherAware {
  private String dataId = "gateway-router";
  private String group = "DEFAULT_GROUP";
  @Value("${spring.cloud.nacos.config.server-addr}")
  private String serverAddr;
  @Autowired
  private RouteDefinitionWriter routeDefinitionWriter;
  private ApplicationEventPublisher applicationEventPublisher;
  private static final List<String> ROUTE_LIST = new ArrayList<>();
  @PostConstruct
  public void dynamicRouteByNacosListener() {
    try {
      ConfigService configService = NacosFactory.createConfigService(serverAddr);
      configService.getConfig(dataId, group, 5000);
      configService.addListener(dataId, group, new Listener() {
        @Override
        public void receiveConfigInfo(String configInfo) {
          clearRoute();
          try {
            if (StringUtil.isNullOrEmpty(configInfo)) {//配置被刪除
              return;
            }
            List<RouteDefinition> gatewayRouteDefinitions = JSONObject.parseArray(configInfo, RouteDefinition.class);
            for (RouteDefinition routeDefinition : gatewayRouteDefinitions) {
              addRoute(routeDefinition);
            }
            publish();
          } catch (Exception e) {
            log.error("receiveConfigInfo error" + e);
          }
        }
        @Override
        public Executor getExecutor() {
          return null;
        }
      });
    } catch (NacosException e) {
        log.error("dynamicRouteByNacosListener error" + e);
    }
  }
  private void clearRoute() {
    for (String id : ROUTE_LIST) {
      this.routeDefinitionWriter.delete(Mono.just(id)).subscribe();
    }
    ROUTE_LIST.clear();
  }
  private void addRoute(RouteDefinition definition) {
    try {
      routeDefinitionWriter.save(Mono.just(definition)).subscribe();
      ROUTE_LIST.add(definition.getId());
    } catch (Exception e) {
 log.error("addRoute error" + e);
    }
  }
  private void publish() {
    this.applicationEventPublisher.publishEvent(new RefreshRoutesEvent(this.routeDefinitionWriter));
  }
  @Override
  public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
    this.applicationEventPublisher = applicationEventPublisher;
  }
  • 在nacos中增加一個(gè)規(guī)則:
[{
    "filters": [],
    "id": "baidu_route",
    "order": 0,
    "predicates": [{
        "args": {
            "pattern": "/baidu"
        },
        "name": "Path"
    }],
    "uri": "https://www.baidu.com"
}]
  • 訪(fǎng)問(wèn)網(wǎng)關(guān)的路由規(guī)則,能看到剛剛加入的規(guī)則竭钝,訪(fǎng)問(wèn)http://localhost:9022/baidu時(shí)請(qǐng)求直接被轉(zhuǎn)發(fā)到百度的首頁(yè)了墓贿。
    2.png

基礎(chǔ)配置:

  • 現(xiàn)在的請(qǐng)求通過(guò)經(jīng)過(guò)gateWay網(wǎng)關(guān)時(shí),需要在網(wǎng)關(guān)統(tǒng)一配置跨域請(qǐng)求蜓氨,需求所有請(qǐng)求通過(guò)
spring:
  cloud:
    gateway:
      globalcors:
        cors-configurations:
          '[/**]':
            allowed-origins: "*"
            allowed-headers: "*"
            allow-credentials: true
            allowed-methods:
              - GET
              - POST
              - DELETE
              - PUT
              - OPTION
  • eureka、admin-client队伟、actuator健康檢查配置穴吹,為之后的功能提供支持,此部分比較簡(jiǎn)單嗜侮,不再贅述港令,加入以下maven依賴(lài)和配置
## maven依賴(lài)
      <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
     </dependency>
    <dependency>
            <groupId>de.codecentric</groupId>
            <artifactId>spring-boot-admin-starter-client</artifactId>
            <version>2.1.0</version>
        </dependency>
    <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>
##配置項(xiàng)
spring:
  application:
    name: mas-cloud-gateway
  boot:
    admin:
      client:
      ### 本地搭建的admin-server
        url: http://localhost:8011
eureka:
  client:
    registerWithEureka: true
    fetchRegistry: true
    healthcheck:
      enabled: true
    serviceUrl:
      defaultZone: http://localhost:6887/eureka/
    enabled: true
feign:
  sentinel:
    enabled: true
management:
  endpoints:
    web:
      exposure:
        include: '*'
  endpoint:
    health:
      show-details: ALWAYS
  • 若轉(zhuǎn)發(fā)的目標(biāo)地址為微服務(wù)中組件啥容,不為具體ip:port形式的,應(yīng)寫(xiě)成lb://mas-openapi-service形式顷霹,目標(biāo)地址會(huì)從注冊(cè)中心直接拉取

Sentinel

使用Sentinel作為gateWay的限流咪惠、降級(jí)、系統(tǒng)保護(hù)工具

基本概念

  • 資源
資源是 Sentinel 的關(guān)鍵概念淋淀。它可以是 Java 應(yīng)用程序中的任何內(nèi)容遥昧,例如,由應(yīng)用程序提供的服務(wù)朵纷,或由應(yīng)用程序調(diào)用的其它應(yīng)用提供的服務(wù)炭臭,甚至可以是一段代碼。在接下來(lái)的文檔中都會(huì)用資源來(lái)描述代碼塊袍辞。只要通過(guò) Sentinel API 定義的代碼鞋仍,就是資源,能夠被 Sentinel 保護(hù)起來(lái)搅吁。大部分情況下威创,可以使用方法簽名,URL谎懦,甚至服務(wù)名稱(chēng)作為資源名來(lái)標(biāo)示資源肚豺。
  • 規(guī)則
圍繞資源的實(shí)時(shí)狀態(tài)設(shè)定的規(guī)則莉御,可以包括流量控制規(guī)則并闲、熔斷降級(jí)規(guī)則以及系統(tǒng)保護(hù)規(guī)則。所有規(guī)則可以動(dòng)態(tài)實(shí)時(shí)調(diào)整晰绎。

依賴(lài)

     <!--alibaba 流量衛(wèi)士-->
        <dependency>
            <groupId>com.alibaba.csp</groupId>
            <artifactId>sentinel-core</artifactId>
            <version>${sentinel.version}</version>
        </dependency>

同時(shí)也將SpringCloud寞奸、GateWay整合呛谜,加入如下配置

        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
        </dependency>
       <dependency>
            <groupId>com.alibaba.csp</groupId>
            <artifactId>sentinel-spring-cloud-gateway-adapter</artifactId>
            <version>1.7.1</version>
        </dependency>
  • 配置
    • 由于sentinel的工作原理其實(shí)借助于全局的filter進(jìn)行請(qǐng)求攔截并計(jì)算出是否進(jìn)行限流、熔斷等操作的枪萄,增加SentinelGateWayFilter配置
  @Bean//攔截請(qǐng)求
  @Order(Ordered.HIGHEST_PRECEDENCE)
  public GlobalFilter sentinelGatewayFilter() {
    return new SentinelGatewayFilter();
  }
  • sentinel 不僅支持通過(guò)硬代碼方式進(jìn)行資源的申明隐岛,還能通過(guò)注解方式進(jìn)行聲明,為了讓注解生效瓷翻,還需要配置切面類(lèi)SentinelResourceAspect
   @Bean
  public SentinelResourceAspect sentinelResourceAspect() {
    return new SentinelResourceAspect();
  }
  • sentinel攔截包括了視圖聚凹、靜態(tài)資源等,需要配置viewResolvers以及攔截之后的異常齐帚,我們也可以自定義拋出異常的提示
  public SentinelConfig(ObjectProvider<List<ViewResolver>> viewResolversProvider,
                        ServerCodecConfigurer serverCodecConfigurer) {
    this.viewResolvers = viewResolversProvider.getIfAvailable(Collections::emptyList);
    this.serverCodecConfigurer = serverCodecConfigurer;
  }

  @Bean//自定義異常
  @Order(Ordered.HIGHEST_PRECEDENCE)
  public ExceptionHandler sentinelGatewayBlockExceptionHandler() {
    // Register the block exception handler for Spring Cloud Gateway.
    return new ExceptionHandler(viewResolvers, serverCodecConfigurer);
  }
  • 自定義異常提示:當(dāng)發(fā)生限流妒牙、熔斷異常時(shí),會(huì)返回定義的提示信息对妄。
  private Mono<Void> writeResponse(ServerResponse response, ServerWebExchange exchange) {
    ServerHttpResponse serverHttpResponse = exchange.getResponse();
    serverHttpResponse.getHeaders().add("Content-Type", "application/json;charset=UTF-8");
    MasResponse<String> stringMasResponse = MasResponse.fail(
        "限流了"
    );
    byte[] datas = JSON.toJSONString(stringMasResponse).getBytes(StandardCharsets.UTF_8);
    DataBuffer buffer = serverHttpResponse.bufferFactory().wrap(datas);
    return serverHttpResponse.writeWith(Mono.just(buffer));
  }
    不需要額外的配置湘今,sentinel就已經(jīng)可以正常工作了

網(wǎng)關(guān)流控實(shí)現(xiàn)原理

當(dāng)通過(guò) GatewayRuleManager 加載網(wǎng)關(guān)流控規(guī)則(GatewayFlowRule)時(shí),無(wú)論是否針對(duì)請(qǐng)求屬性進(jìn)行限流剪菱,Sentinel 底層都會(huì)將網(wǎng)關(guān)流控規(guī)則轉(zhuǎn)化為熱點(diǎn)參數(shù)規(guī)則(ParamFlowRule)摩瞎,存儲(chǔ)在GatewayRuleManager 中拴签,與正常的熱點(diǎn)參數(shù)規(guī)則相隔離。轉(zhuǎn)換時(shí) Sentinel 會(huì)根據(jù)請(qǐng)求屬性配置旗们,為網(wǎng)關(guān)流控規(guī)則設(shè)置參數(shù)索引(idx)蚓哩,并同步到生成的熱點(diǎn)參數(shù)規(guī)則中。

外部請(qǐng)求進(jìn)入 API Gateway 時(shí)會(huì)經(jīng)過(guò) Sentinel 實(shí)現(xiàn)的 filter上渴,其中會(huì)依次進(jìn)行 路由/API 分組匹配岸梨、請(qǐng)求屬性解析和參數(shù)組裝。Sentinel 會(huì)根據(jù)配置的網(wǎng)關(guān)流控規(guī)則來(lái)解析請(qǐng)求屬性驰贷,并依照參數(shù)索引順序組裝參數(shù)數(shù)組盛嘿,最終傳入SphU.entry(res, args) 中。Sentinel API Gateway Adapter Common 模塊向 Slot Chain 中添加了一個(gè) GatewayFlowSlot括袒,專(zhuān)門(mén)用來(lái)做網(wǎng)關(guān)規(guī)則的檢查次兆。GatewayFlowSlot 會(huì)從GatewayRuleManager中提取生成的熱點(diǎn)參數(shù)規(guī)則,根據(jù)傳入的參數(shù)依次進(jìn)行規(guī)則檢查锹锰。若某條規(guī)則不針對(duì)請(qǐng)求屬性芥炭,則會(huì)在參數(shù)最后一個(gè)位置置入預(yù)設(shè)的常量,達(dá)到普通流控的效果恃慧。
3.png
  • 過(guò)濾順序:當(dāng)請(qǐng)求到來(lái)時(shí)园蝠,會(huì)依次調(diào)用下面的規(guī)則進(jìn)行校驗(yàn),而chain繼承自AbstractLinkedProcessorSlot<Object>痢士,將過(guò)濾的規(guī)則依次進(jìn)行直到不通過(guò)或者到最后一個(gè)規(guī)則彪薛。
4.gif
##對(duì)應(yīng)代碼中定義的順序
 ProcessorSlotChain chain = new DefaultProcessorSlotChain();
        // Prepare slot
        chain.addLast(new NodeSelectorSlot());
        chain.addLast(new ClusterBuilderSlot());
        // Stat slot
        chain.addLast(new LogSlot());
        chain.addLast(new StatisticSlot());
        // Rule checking slot
        chain.addLast(new AuthoritySlot());
        chain.addLast(new SystemSlot());
        chain.addLast(new GatewayFlowSlot());
        chain.addLast(new ParamFlowSlot());
        chain.addLast(new FlowSlot());
        chain.addLast(new DegradeSlot());
        return chain;
  • 通過(guò)一層層的插點(diǎn)slot,以達(dá)到統(tǒng)計(jì)怠蹂、限流善延、降級(jí)等功能,各個(gè)slot功能如下(按照?qǐng)?zhí)行順序):
    • NodeSelectorSlot 負(fù)責(zé)收集資源的路徑城侧,并將這些資源的調(diào)用路徑易遣,以樹(shù)狀結(jié)構(gòu)存儲(chǔ)起來(lái),用于根據(jù)調(diào)用路徑來(lái)限流降級(jí)嫌佑;

    • ClusterBuilderSlot 則用于存儲(chǔ)資源的統(tǒng)計(jì)信息以及調(diào)用者信息豆茫,例如該資源的 RT, QPS, thread count 等等,這些信息將用作為多維度限流屋摇,降級(jí)的依據(jù)揩魂;

    • LogSlot:用來(lái)記錄系統(tǒng)日志,當(dāng)前通過(guò)數(shù)量炮温、拒絕數(shù)量等

    • StatistcSlot 則用于記錄火脉,統(tǒng)計(jì)不同緯度的 runtime 信息;

    • AuthorizationSlot 則根據(jù)黑白名單,來(lái)做黑白名單控制忘分;

    • SystemSlot 則通過(guò)系統(tǒng)的狀態(tài),當(dāng)前的運(yùn)行狀態(tài)白修、CPU占用率等妒峦,來(lái)控制總的入口流量;

    • GatewayFlowSlot::網(wǎng)關(guān)限流規(guī)則

    • ParamFlowSlot:參數(shù)值限流規(guī)則定義

    • FlowSlot 則用于根據(jù)預(yù)設(shè)的限流規(guī)則兵睛,以及前面 slot 統(tǒng)計(jì)的狀態(tài)肯骇,來(lái)進(jìn)行限流

    • DegradeSlot 則通過(guò)統(tǒng)計(jì)信息,以及預(yù)設(shè)的規(guī)則祖很,來(lái)做熔斷降級(jí)笛丙;

      注:Sentinel 1.6.0之后的版本引入了 Sentinel API Gateway Adapter Common 模塊,此模塊中包含網(wǎng)關(guān)限流的規(guī)則和自定義 API的實(shí)體和管理邏輯

  • AuthorizationSlot:若未配置任何規(guī)則假颇,所有的請(qǐng)求都將成功通過(guò)胚鸯,規(guī)則字段解釋如下:
resource 指定訪(fǎng)問(wèn)的資源名稱(chēng)
limitApp 限制來(lái)源,可通過(guò)英文逗號(hào)(,)指定多個(gè)來(lái)源限定笨鸡,通常為請(qǐng)求者IP或者消費(fèi)者名稱(chēng)
strategy 限制模式姜钳,白名單還是黑名單,默認(rèn)為白名單形耗,啟用后只有在白名單的來(lái)源才能通過(guò)
### 只允許來(lái)源為127.0.0.1或者localhost的請(qǐng)求才能通過(guò)
   AuthorityRule rule = new AuthorityRule();
    rule.setResource("info");
    rule.setStrategy(RuleConstant.AUTHORITY_WHITE);
    rule.setLimitApp("127.0.0.1,localhost");
    AuthorityRuleManager.loadRules(Collections.singletonList(rule));
###黑名單類(lèi)似哥桥,來(lái)源為指定的值時(shí)將之前拒絕請(qǐng)求
    rule.setStrategy(RuleConstant.AUTHORITY_BLACK);
    rule.setLimitApp("127.0.0.1,localhost");
注:當(dāng)其他的來(lái)源的應(yīng)用訪(fǎng)問(wèn)時(shí),該請(qǐng)求將無(wú)法通過(guò)激涤,如果未查找到該請(qǐng)求來(lái)源將直接通過(guò)拟糕。
  • SystemSlot:系統(tǒng)限制規(guī)則,作為整個(gè)系統(tǒng)的衡量標(biāo)準(zhǔn)倦踢,不需要指定資源名稱(chēng)送滞,字段解釋如下:

|||
|-|-|-|
|highestSystemLoad|最大的Load,目前只針對(duì)Unix/Linux生效硼一,默認(rèn)為-1不生效|
|highestCpuUsage |最高CPU使用率累澡,范圍[0-1],高于該值時(shí)將拒絕所有請(qǐng)求般贼,做降級(jí)處理|
|qps |所有入口資源的QPS愧哟,默認(rèn)為-1不生效|
|avgRt |所有入口流量的平均響應(yīng)時(shí)間,ms為單位|
|maxThread|入口流量的最大并發(fā)數(shù)哼蛆,默認(rèn)為-1不生效|

##限制QPS為3蕊梧,平均返回時(shí)間為200ms
    List<SystemRule> rules = new ArrayList<>();
    SystemRule rule = new SystemRule();
//    rule.setHighestSystemLoad(3.0);//系統(tǒng)負(fù)載  只針對(duì)Linux/unix
    rule.setHighestCpuUsage(-1);//當(dāng)前系統(tǒng)的 CPU 使用率
    rule.setAvgRt(200);//所有入口流量的平均響應(yīng)時(shí)間 ms
    rule.setQps(3);
    rule.setMaxThread(500);
    rules.add(rule);
    SystemRuleManager.loadRules(rules);
訪(fǎng)問(wèn)本地API服務(wù)器,請(qǐng)求第一次時(shí)可以得到正確的返回信息腮介,但是第二次訪(fǎng)問(wèn)時(shí)肥矢,由于上一次的請(qǐng)求耗時(shí)544/2ms,第二次請(qǐng)求將不會(huì)被通過(guò)
請(qǐng)求次數(shù):2
請(qǐng)求路徑:/api/query
訪(fǎng)問(wèn)成功了!甘改! 我是API服務(wù)器
{"echoCode":500,"echoMessage":"限流了","success":false}
544ms

更改系統(tǒng)規(guī)則中的QPS參數(shù)旅东,不限制平均響應(yīng)時(shí)間

    List<SystemRule> rules = new ArrayList<>();
    SystemRule rule = new SystemRule();
//    rule.setHighestSystemLoad(3.0);//系統(tǒng)負(fù)載  只針對(duì)Linux/unix
    rule.setHighestCpuUsage(-1);//當(dāng)前系統(tǒng)的 CPU 使用率
    //rule.setAvgRt(200);//所有入口流量的平均響應(yīng)時(shí)間 ms
    rule.setQps(3);
    rule.setMaxThread(500);
    rules.add(rule);
    SystemRuleManager.loadRules(rules);
由于我們?cè)O(shè)置的QPS為3,那么當(dāng)我們請(qǐng)求10次時(shí)十艾,前三次可以成功請(qǐng)求抵代,但第四次開(kāi)始,請(qǐng)求將不會(huì)被處理忘嫉。
請(qǐng)求次數(shù):10
請(qǐng)求路徑:/api/query
訪(fǎng)問(wèn)成功了;珉埂! 我是API服務(wù)器
訪(fǎng)問(wèn)成功了G烀帷康吵! 我是API服務(wù)器
訪(fǎng)問(wèn)成功了!访递! 我是API服務(wù)器
訪(fǎng)問(wèn)成功了;耷丁! 我是API服務(wù)器
{"echoCode":500,"echoMessage":"限流了","success":false}
{"echoCode":500,"echoMessage":"限流了","success":false}
{"echoCode":500,"echoMessage":"限流了","success":false}
{"echoCode":500,"echoMessage":"限流了","success":false}
{"echoCode":500,"echoMessage":"限流了","success":false}
{"echoCode":500,"echoMessage":"限流了","success":false}
530ms
  • GatewayFlowRule:網(wǎng)關(guān)限流規(guī)則力九,針對(duì) API Gateway 的場(chǎng)景定制的限流規(guī)則耍铜,可以針對(duì)不同 route 或自定義的 API 分組進(jìn)行限流,支持針對(duì)請(qǐng)求中的參數(shù)跌前、Header棕兼、來(lái)源 IP 等進(jìn)行定制化的限流。其中網(wǎng)關(guān)限流規(guī)則 GatewayFlowRule 的字段解釋如下:
resource 資源名稱(chēng)抵乓,可以是網(wǎng)關(guān)中的 route 名稱(chēng)或者用戶(hù)自定義的API 分組名稱(chēng)伴挚。
resourceMode 規(guī)則是針對(duì) API Gateway 的route(RESOURCE_MODE_ROUTE_ID)還是用戶(hù)在 Sentinel 中定義的API 分組(RESOURCE_MODE_CUSTOM_API_NAME),默認(rèn)是route灾炭。
grade: 限流指標(biāo)維度茎芋,同限流規(guī)則的grade 字段。
count: 限流閾值
intervalSec: 統(tǒng)計(jì)時(shí)間窗口蜈出,單位是秒田弥,默認(rèn)是1 秒(目前僅對(duì)參數(shù)限流生效)。
controlBehavior 流量整形的控制效果铡原,同限流規(guī)則的 controlBehavior 字段偷厦,目前支持快速失敗和勻速排隊(duì)兩種模式,默認(rèn)是快速失敗燕刻。
burst: 應(yīng)對(duì)突發(fā)請(qǐng)求時(shí)額外允許的請(qǐng)求數(shù)目(目前僅對(duì)參數(shù)限流生效)只泼。
maxQueueingTimeoutMs: 勻速排隊(duì)模式下的最長(zhǎng)排隊(duì)時(shí)間,單位是毫秒卵洗,僅在勻速排隊(duì)模式下生效请唱。
paramItem: 參數(shù)限流配置。若不提供,則代表不針對(duì)參數(shù)進(jìn)行限流十绑,該網(wǎng)關(guān)規(guī)則將會(huì)被轉(zhuǎn)換成普通流控規(guī)則聚至;否則會(huì)轉(zhuǎn)換成熱點(diǎn)規(guī)則。其中的字段:
parseStrategy: 從請(qǐng)求中提取參數(shù)的策略本橙,目前支持提取來(lái)源 IP(PARAM_PARSE_STRATEGY_CLIENT_IP)晚岭、Host(PARAM_PARSE_STRATEGY_HOST)、任意 Header(PARAM_PARSE_STRATEGY_HEADER)和任意 URL 參數(shù)(PARAM_PARSE_STRATEGY_URL_PARAM)四種模式勋功。
fieldName: 若提取策略選擇 Header 模式或 URL 參數(shù)模式,則需要指定對(duì)應(yīng)的 header 名稱(chēng)或 URL 參數(shù)名稱(chēng)库说。
pattern 和 matchStrategy: 為后續(xù)參數(shù)匹配特性預(yù)留狂鞋,目前未實(shí)現(xiàn)。
  • 根據(jù)GateWay提供的特性潜的,可設(shè)置兩個(gè)維度的限流和熔斷骚揍,指定API或者轉(zhuǎn)發(fā)路由,為了驗(yàn)證通過(guò)性啰挪,通過(guò)GateWay路由分別路由服務(wù)器CMS和PRODUCT服務(wù)器信不,并都將它們都注冊(cè)到Eureka中,配置規(guī)則如下:
## 將包含content請(qǐng)求路徑都轉(zhuǎn)發(fā)到CMS服務(wù)器上,其他求都轉(zhuǎn)發(fā)到product服務(wù)器上
        - id: cms_route
          uri: lb://mas-cms-service
          predicates:
            - Path=/{tenant}/service/content/**
        - id: product_route
          uri: lb://mas-product-service
          predicates:
            - Path=/**
路由轉(zhuǎn)發(fā)正常
5.png
  • 自定義路由攔截規(guī)則:在上圖中我們定義并請(qǐng)求了三個(gè)路徑亡呵,其中前面兩個(gè)都是對(duì)CMS服務(wù)器的請(qǐng)求抽活,所以對(duì)于前面兩個(gè)請(qǐng)求對(duì)應(yīng)的路由資源為cms_route,第三個(gè)請(qǐng)求的路由資源為product_route锰什,限定第一個(gè)請(qǐng)求的路徑每秒QPS為3時(shí):
    Set<GatewayFlowRule> rules = new HashSet<>();
    rules.add(new GatewayFlowRule("cms_route")
        .setResourceMode(SentinelGatewayConstants.RESOURCE_MODE_ROUTE_ID)//路由規(guī)則
        .setCount(3)
        .setGrade(RuleConstant.FLOW_GRADE_QPS)//限制規(guī)則為QPS
        .setIntervalSec(1)
    );
      GatewayRuleManager.loadRules(rules);
針對(duì)同一個(gè)ROUTE_ID的請(qǐng)求下硕,后面的將會(huì)被攔截,而不同ROUTE_ID的請(qǐng)求將不會(huì)受到影響汁胆。
7.png
  • 自定義API攔截規(guī)則:若只攔截以{tenant}/service/content/place形式的接口時(shí)梭姓,需要根據(jù)正則表達(dá)式匹配該模式的請(qǐng)求地址并將其聲明為可攔截的API資源,再設(shè)定限流嫩码、熔斷規(guī)則
    Set<ApiDefinition> definitions = new HashSet<>();
    //顯式申明API
    ApiDefinition placeApi = new ApiDefinition("place_flow")
        .setPredicateItems(new HashSet<ApiPredicateItem>() {{
          add(new ApiPathPredicateItem().setPattern("/lh9999/service/content/place/**")
              .setMatchStrategy(SentinelGatewayConstants.URL_MATCH_STRATEGY_PREFIX));
        }});
    definitions.add(placeApi);
    GatewayApiDefinitionManager.loadApiDefinitions(definitions);
    //設(shè)置規(guī)則
      Set<GatewayFlowRule> rules = new HashSet<>();
    rules.add(new GatewayFlowRule("place_flow")
        .setResourceMode(SentinelGatewayConstants.RESOURCE_MODE_CUSTOM_API_NAME)
        .setCount(3)
        .setGrade(RuleConstant.FLOW_GRADE_QPS)
        .setIntervalSec(1)
    );
    GatewayRuleManager.loadRules(rules);
  • 當(dāng)同時(shí)請(qǐng)求服務(wù)器時(shí)誉尖,只要路徑符合**/lh9999/service/content/place/****形式的請(qǐng)求都適用上面的規(guī)則,這里我們使用線(xiàn)程池模擬并發(fā)請(qǐng)求铸题,請(qǐng)求第四次時(shí)將直接失敗铡恕,限流效果還是很明顯的。


    6.png
  • 熱點(diǎn)參數(shù)規(guī)則(ParamFlowRule)類(lèi)似于流量控制規(guī)則(FlowRule)

屬性 說(shuō)明 默認(rèn)值
resource 資源名回挽,必填
count 限流閾值没咙,必填
grade 限流模式 QPS 模式
paramIdx 熱點(diǎn)參數(shù)的索引,必填千劈,對(duì)應(yīng) SphU.entry(xxx, args) 中的參數(shù)索引位置
paramFlowItemList 參數(shù)例外項(xiàng)祭刚,可以針對(duì)指定的參數(shù)值單獨(dú)設(shè)置限流閾值,不受前面 count 閾值的限制。僅支持基本類(lèi)型
clusterMode 是否是集群參數(shù)流控規(guī)則 false
clusterConfig 集群流控相關(guān)配置
  • 熱點(diǎn):經(jīng)常被訪(fǎng)問(wèn)的數(shù)據(jù)涡驮,作為公共資源暗甥,很多時(shí)候,我們希望統(tǒng)計(jì)訪(fǎng)問(wèn)某個(gè)熱點(diǎn)數(shù)據(jù)中訪(fǎng)問(wèn)頻次最高的幾項(xiàng)數(shù)據(jù)捉捅,并將其設(shè)置訪(fǎng)問(wèn)限制撤防,例如:
    • 商品ID:針對(duì)某個(gè)熱點(diǎn)商品ID設(shè)置訪(fǎng)問(wèn)限制
    • 用戶(hù)ID:針對(duì)某個(gè)熱點(diǎn)用戶(hù)ID設(shè)置訪(fǎng)問(wèn)限制

熱點(diǎn)參數(shù)限流會(huì)統(tǒng)計(jì)傳入?yún)?shù)中的熱點(diǎn)參數(shù),并根據(jù)配置的限流閾值與模式棒口,對(duì)包含熱點(diǎn)參數(shù)的資源調(diào)用進(jìn)行限流寄月。熱點(diǎn)參數(shù)限流可以看做是一種特殊的流量控制,僅對(duì)包含熱點(diǎn)參數(shù)的資源調(diào)用生效无牵。

  • 可以通過(guò) SphU 類(lèi)里面幾個(gè) entry 重載方法來(lái)傳入對(duì)應(yīng)的參數(shù)以便 Sentinel 統(tǒng)計(jì),為對(duì)應(yīng)的資源配置熱點(diǎn)參數(shù)限流規(guī)則漾肮,并在 entry 的時(shí)候傳入相應(yīng)的參數(shù),即可使熱點(diǎn)參數(shù)限流生效茎毁。
public static Entry entry(String name, EntryType type, int count, Object... args) throws BlockException

public static Entry entry(Method method, EntryType type, int count, Object... args) throws BlockException
  • 實(shí)例:若一個(gè)用戶(hù)短時(shí)間內(nèi)登錄次數(shù)過(guò)多克懊,將限制其登錄行為,防止惡意登錄
  @PostMapping("{account}/checkLogin")
@SentinelResource("checkLogin")
  public BaasResponse<String> checkLogin(@PathVariable(value = "account") String account, @RequestBody String password) {
    try {
      SphU.entry("checkLogin", EntryType.IN, 1, account);
      ...chekcLogin
    } catch (BlockException e) {
      BaasResponse<String> response = new BaasResponse<>();
      response.setSuccess(false);
      response.setEchoMessage(account + "被限制登錄");
      return response;
    }
    return BaasResponse.success(account + "登錄成功");
  }

制定限制規(guī)則七蜘,時(shí)間窗口限制為1s谭溉,在這1s中所有用戶(hù)最多允許登錄5次,通過(guò)ParamFlowRuleManager.loadRuls我們可以很容易制定限流規(guī)則

List<ParamFlowRule> rules = new ArrayList<>();
    ParamFlowRule rule = new ParamFlowRule();
    //閾值類(lèi)型:只支持QPS
    rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
    //閾值
    rule.setCount(5);
    //資源名
    rule.setResource("checkLogin");
    rule.setParamIdx(0);//指配熱點(diǎn)參數(shù)的下標(biāo)
    //統(tǒng)計(jì)窗口時(shí)間長(zhǎng)度
 rule.setDurationInSec(1);
    List<ParamFlowItem> items = new ArrayList<>();
    ParamFlowItem item = new ParamFlowItem();
    item.setClassType(String.class.getTypeName());
    item.setCount(5);
    items.add(item);
    rule.setParamFlowItemList(items);
    rules.add(rule);
    ParamFlowRuleManager.loadRules(rules);

同一時(shí)刻tom橡卤、jack同時(shí)登錄扮念,tom登錄兩次,jack登錄了10次碧库,那么根據(jù)我們制定的規(guī)則扔亥,jack的行為將會(huì)被限制,其他用戶(hù)將不會(huì)受到影響


8.png

Sentinel圖形界面

如果上面的代碼配置不夠靈活可以通過(guò)Sentinel提供的用戶(hù)界面sentinel-dashboard實(shí)時(shí)進(jìn)行規(guī)則的制定谈为,可同時(shí)管理多個(gè)客戶(hù)端
  • 客戶(hù)端配置:在配置文件中增加下列配置旅挤,dashboard就可以輕松管理客戶(hù)端了,還有一種方式是在啟動(dòng)時(shí)加入
spring:
  cloud:
    sentinel:
      transport:
        ## VM
        ##-Djava.net.preferIPv4Stack=true -Dcsp.sentinel.dashboard.server=localhost:8080 -Dcsp.sentinel.api.port=8666 -Dproject.name=gateway -Dcsp.sentinel.app.type=1
        dashboard: localhost:8880
        port: 8880

下載完成可直接啟動(dòng)伞鲫,java -jar sentinel-dashboard.jar粘茄,登錄dashboard,就可以很清楚的看到眾多限流秕脓、熔斷等功能


9.png

同時(shí)對(duì)于剛剛建立的熱點(diǎn)參數(shù)柒瓣,還有其他特殊的限制功能,如圖所示吠架,可以限制某些特殊值不處理:


熱點(diǎn)參數(shù)特殊規(guī)則
  • 規(guī)則持久化:在dashboard中配置的規(guī)則都是存儲(chǔ)在內(nèi)存中的芙贫,dashboard 是通過(guò) transport 模塊來(lái)獲取每個(gè) Sentinel 客戶(hù)端中的規(guī)則的,獲取到的規(guī)則通過(guò) RuleRepository 接口保存在 Dashboard 的內(nèi)存中傍药,如果在 Dashboard 頁(yè)面中更改了某個(gè)規(guī)則磺平,也會(huì)調(diào)用 transport 模塊提供的接口將規(guī)則更新到客戶(hù)端中去魂仍。
    試想這樣一種情況,客戶(hù)端連接上 Dashboard 之后拣挪,我們?cè)?Dashboard 上為客戶(hù)端配置好了規(guī)則擦酌,并推送給了客戶(hù)端。這時(shí)由于一些因素客戶(hù)端出現(xiàn)異常菠劝,服務(wù)不可用了赊舶,當(dāng)客戶(hù)端恢復(fù)正常再次連接上 Dashboard 后,這時(shí)所有的規(guī)則都丟失了赶诊,我們還需要重新配置一遍規(guī)則笼平,這肯定不是我們想要的。

  • 把原本保存在 內(nèi)存中的規(guī)則舔痪,持久化一份副本出去出吹。這樣下次客戶(hù)端重啟后,可以從持久化的副本中把數(shù)據(jù) load 進(jìn)內(nèi)存中辙喂,這樣就不會(huì)丟失規(guī)則了,如下圖所示:


    持久化原理
  • Sentinel 為我們提供了兩個(gè)接口來(lái)實(shí)現(xiàn)規(guī)則的持久化鸠珠,他們分別是:ReadableDataSource 和 WritableDataSource巍耗。因?yàn)橥ǔ8鞣N持久化的數(shù)據(jù)源已經(jīng)提供了具體的將數(shù)據(jù)持久化的方法了,我們只需要把數(shù)據(jù)從持久化的數(shù)據(jù)源中獲取出來(lái)渐排,轉(zhuǎn)成我們需要的格式就可以了炬太。
    下面我們來(lái)看一下 ReadableDataSource 接口的具體的定義:

public interface ReadableDataSource<S, T> {
    // 從數(shù)據(jù)源中讀取原始的數(shù)據(jù)
    S readSource() throws Exception;
    // 將原始數(shù)據(jù)轉(zhuǎn)換成我們所需的格式
    T loadConfig() throws Exception;
    // 獲取該種數(shù)據(jù)源的SentinelProperty對(duì)象
    SentinelProperty<T> getProperty();
}
接口很簡(jiǎn)單,最重要的就是這三個(gè)方法驯耻,另外 Sentinel 還為我們提供了一個(gè)抽象類(lèi):AbstractDataSource亲族,該抽象類(lèi)中實(shí)現(xiàn)了兩個(gè)方法,具體的數(shù)據(jù)源實(shí)現(xiàn)類(lèi)只需要實(shí)現(xiàn)一個(gè) readSource 方法即可可缚,具體的代碼如下:
public abstract class AbstractDataSource<S, T> 
        implements ReadableDataSource<S, T> {
    // Converter接口負(fù)責(zé)轉(zhuǎn)換數(shù)據(jù)
    protected final Converter<S, T> parser;
    // SentinelProperty接口負(fù)責(zé)觸發(fā)PropertyListener
    // 的configUpdate方法的回調(diào)
    protected final SentinelProperty<T> property;

    public AbstractDataSource(Converter<S, T> parser) {
        if (parser == null) {
            throw new IllegalArgumentException("parser can't be null");
        }
        this.parser = parser;
        this.property = new DynamicSentinelProperty<T>();
    }
    @Override
    public T loadConfig() throws Exception {
        return loadConfig(readSource());
    }
    public T loadConfig(S conf) throws Exception {
        return parser.convert(conf);
    }
    @Override
    public SentinelProperty<T> getProperty() {
        return property;
    }
}
  • 每個(gè)具體的 DataSource 實(shí)現(xiàn)類(lèi)需要做三件事:

    • 實(shí)現(xiàn) readSource 方法將數(shù)據(jù)源中的原始數(shù)據(jù)轉(zhuǎn)換成我們可以處理的數(shù)據(jù)S
    • 提供一個(gè) Converter 來(lái)將數(shù)據(jù)S轉(zhuǎn)換成最終的數(shù)據(jù)T
    • 將最終的數(shù)據(jù)T更新到具體的 RuleManager 中去
  • DataSource 擴(kuò)展常見(jiàn)的實(shí)現(xiàn)方式有:

    拉模式:客戶(hù)端主動(dòng)向某個(gè)規(guī)則管理中心定期輪詢(xún)拉取規(guī)則霎迫,這個(gè)規(guī)則中心可以是 RDBMS、文件帘靡,甚至是 VCS 等知给。這樣做的方式是簡(jiǎn)單,缺點(diǎn)是無(wú)法及時(shí)獲取變更描姚;
    推模式:規(guī)則中心統(tǒng)一推送涩赢,客戶(hù)端通過(guò)注冊(cè)監(jiān)聽(tīng)器的方式時(shí)刻監(jiān)聽(tīng)變化,比如使用 Nacos轩勘、Zookeeper 等配置中心筒扒。這種方式有更好的實(shí)時(shí)性和一致性保證。
  • Sentinel 目前支持以下數(shù)據(jù)源擴(kuò)展:能保證更新規(guī)則時(shí)绊寻,客戶(hù)端能得到通知即可
    規(guī)則的更新可以通過(guò) Sentinel Dashboard 也可以通過(guò)各個(gè)配置中心自己的更新接口來(lái)操作
    AbstractDataSource 中的 SentinelProperty 持有了一個(gè) PropertyListener 接口花墩,最終更新 RuleManager 中的規(guī)則是 PropertyListener 中實(shí)現(xiàn)的
    Pull-based: 文件悬秉、Consul
    Push-based: ZooKeeper, Redis, Nacos, Apollo, etcd
  • 規(guī)則持久化:對(duì)這5種方式一一進(jìn)行了解,以持久化限流的規(guī)則為例观游。
  • File
    文件持久化有一個(gè)問(wèn)題就是文件不像其他的配置中心搂捧,數(shù)據(jù)發(fā)生變更后會(huì)發(fā)出通知,使用文件來(lái)持久化的話(huà)就需要我們自己定時(shí)去掃描文件懂缕,來(lái)確定文件是否發(fā)現(xiàn)了變更允跑。
    文件數(shù)據(jù)源是通過(guò) FileRefreshableDataSource 類(lèi)來(lái)實(shí)現(xiàn)的,他是通過(guò)文件的最后更新時(shí)間來(lái)判斷規(guī)則是否發(fā)生變更的搪柑。
    首先需要引入依賴(lài):
<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-datasource-extension</artifactId>
</dependency>

接入的方法如下:

private void init() throws Exception {
    // 保存了限流規(guī)則的文件的地址
    String flowRuleName = yourFlowRuleFileName();
    Converter<String, List<FlowRule>> parser = source -> JSON.parseObject(source,new TypeReference<List<FlowRule>>() {});
    // 創(chuàng)建文件規(guī)則數(shù)據(jù)源
    FileRefreshableDataSource<List<FlowRule>> flowRuleDataSource = new FileRefreshableDataSource<>(flowRuleName, parser);
    // 將Property注冊(cè)到 RuleManager 中去
    FlowRuleManager.register2Property(flowRuleDataSource.getProperty());
}
  • 在系統(tǒng)啟動(dòng)的時(shí)候調(diào)用該數(shù)據(jù)源注冊(cè)的方法聋丝,否則不會(huì)生效的。具體的方式有很多工碾,可以借助 Spring 來(lái)初始化該方法弱睦,也可以自定義一個(gè)類(lèi)來(lái)實(shí)現(xiàn) Sentinel 中的 InitFunc 接口來(lái)完成初始化。
    Sentinel 會(huì)在系統(tǒng)啟動(dòng)的時(shí)候通過(guò) spi 來(lái)掃描 InitFunc 的實(shí)現(xiàn)類(lèi)渊额,并執(zhí)行 InitFunc 的 init 方法

  • Redis 數(shù)據(jù)源的實(shí)現(xiàn)類(lèi)是 RedisDataSource况木。
    首先引入依賴(lài):

<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-datasource-redis</artifactId>
</dependency>
接入方法如下:
private void init() throws Exception {
    Converter<String, List<FlowRule>> parser = source -> JSON.parseObject(source,new TypeReference<List<FlowRule>>() {});
    RedisConnectionConfig config = RedisConnectionConfig.builder()
        .withHost(redisHost)
        .withPort(redisPort)
        .build();
    ReadableDataSource<String, List<FlowRule>> redisDataSource = new RedisDataSource<>(config, ruleKey, channel, parser);
    FlowRuleManager.register2Property(redisDataSource.getProperty());
}
  • Nacos
    Nacos 數(shù)據(jù)源的實(shí)現(xiàn)類(lèi)是 NacosDataSource。
    首先引入依賴(lài):
<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-datasource-nacos</artifactId>
</dependency>
接入方法如下:
private void init() throws Exception {
    Converter<String, List<FlowRule>> parser = source -> JSON.parseObject(source,new TypeReference<List<FlowRule>>() {});
    ReadableDataSource<String, List<FlowRule>> nacosDataSource = new NacosDataSource<>(remoteAddress, groupId, dataId, parser);
    FlowRuleManager.register2Property(nacosDataSource.getProperty());
}
  • Zk
    Zk 數(shù)據(jù)源的實(shí)現(xiàn)類(lèi)是 ZookeeperDataSource旬迹。
    首先引入依賴(lài):
<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-datasource-zookeeper</artifactId>
</dependency>
接入方法如下:
private void init() throws Exception {
    String remoteAddress = yourRemoteAddress();
    String path = yourPath();
    Converter<String, List<FlowRule>> parser = source -> JSON.parseObject(source,new TypeReference<List<FlowRule>>() {});
    ReadableDataSource<String, List<FlowRule>> zookeeperDataSource = new ZookeeperDataSource<>(remoteAddress, path, parser);
    FlowRuleManager.register2Property(zookeeperDataSource.getProperty());
}
  • Apollo
    Apollo 數(shù)據(jù)源的實(shí)現(xiàn)類(lèi)是 ApolloDataSource火惊。
    首先引入依賴(lài):
<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-datasource-apollo</artifactId>
</dependency>
接入方法如下:
private void init() throws Exception {
    Converter<String, List<FlowRule>> parser = source -> JSON.parseObject(source,new TypeReference<List<FlowRule>>() {});
    ReadableDataSource<String, List<FlowRule>> apolloDataSource = new ApolloDataSource<>(namespaceName, ruleKey, path, defaultRules);
    FlowRuleManager.register2Property(apolloDataSource.getProperty());
}
可以看到5種持久化的方式基本上大同小異,主要還是對(duì)接每種配置中心奔垦,實(shí)現(xiàn)數(shù)據(jù)的轉(zhuǎn)換屹耐,并且監(jiān)聽(tīng)配置中心的數(shù)據(jù)變化,當(dāng)接收到數(shù)據(jù)變化后能夠及時(shí)的將最新的規(guī)則更新到 RuleManager 中去就可以了椿猎。其他規(guī)則類(lèi)似惶岭,可通過(guò)解析type進(jìn)行區(qū)分。

注:https://github.com/alibaba/Sentinel/tree/master/sentinel-dashboard

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末犯眠,一起剝皮案震驚了整個(gè)濱河市按灶,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌筐咧,老刑警劉巖兆衅,帶你破解...
    沈念sama閱讀 211,639評(píng)論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異嗜浮,居然都是意外死亡羡亩,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,277評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門(mén)危融,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)畏铆,“玉大人,你說(shuō)我怎么就攤上這事吉殃〈蔷樱” “怎么了楷怒?”我有些...
    開(kāi)封第一講書(shū)人閱讀 157,221評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀(guān)的道長(zhǎng)瓦灶。 經(jīng)常有香客問(wèn)我鸠删,道長(zhǎng),這世上最難降的妖魔是什么贼陶? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 56,474評(píng)論 1 283
  • 正文 為了忘掉前任刃泡,我火速辦了婚禮,結(jié)果婚禮上碉怔,老公的妹妹穿的比我還像新娘烘贴。我一直安慰自己,他們只是感情好撮胧,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,570評(píng)論 6 386
  • 文/花漫 我一把揭開(kāi)白布桨踪。 她就那樣靜靜地躺著,像睡著了一般芹啥。 火紅的嫁衣襯著肌膚如雪锻离。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 49,816評(píng)論 1 290
  • 那天墓怀,我揣著相機(jī)與錄音汽纠,去河邊找鬼。 笑死捺疼,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的永罚。 我是一名探鬼主播啤呼,決...
    沈念sama閱讀 38,957評(píng)論 3 408
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼呢袱!你這毒婦竟也來(lái)了官扣?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書(shū)人閱讀 37,718評(píng)論 0 266
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤羞福,失蹤者是張志新(化名)和其女友劉穎惕蹄,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體治专,經(jīng)...
    沈念sama閱讀 44,176評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡卖陵,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,511評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了张峰。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片泪蔫。...
    茶點(diǎn)故事閱讀 38,646評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖喘批,靈堂內(nèi)的尸體忽然破棺而出撩荣,到底是詐尸還是另有隱情铣揉,我是刑警寧澤,帶...
    沈念sama閱讀 34,322評(píng)論 4 330
  • 正文 年R本政府宣布餐曹,位于F島的核電站逛拱,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏台猴。R本人自食惡果不足惜朽合,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,934評(píng)論 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望卿吐。 院中可真熱鬧旁舰,春花似錦、人聲如沸嗡官。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 30,755評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)衍腥。三九已至磺樱,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間婆咸,已是汗流浹背竹捉。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 31,987評(píng)論 1 266
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留尚骄,地道東北人块差。 一個(gè)月前我還...
    沈念sama閱讀 46,358評(píng)論 2 360
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像倔丈,于是被迫代替她去往敵國(guó)和親憨闰。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,514評(píng)論 2 348