Spring Cloud Alibaba系列(六)sentinel的實際應用

1、sentinel的持久化配置

上一章中咱們經過Dashboard來爲Sentinel客戶端設置各類各樣的規則,可是這些規則默認是存放在內存中,極不穩定,沒法用於生成環境,因此須要將其持久化。html

DataSource 擴展常見的實現方式有:java

  • 拉模式:客戶端主動向某個規則管理中心按期輪詢拉取規則,這個規則中心能夠是 RDBMS、文件,甚至是 VCS 等。這樣作的方式是簡單,缺點是沒法及時獲取變動;
  • 推模式:規則中心統一推送,客戶端經過註冊監聽器的方式時刻監聽變化,好比使用 Nacos、Zookeeper 等配置中心。這種方式有更好的實時性和一致性保證。

Sentinel 目前支持如下數據源擴展:git

生產環境中通常經常使用的就是推模式。這裏咱們使用Nacos存儲規則。推送模式的正確作法應該是 配置中心控制檯/Sentinel 控制檯 → 配置中心 → Sentinel 數據源 → Sentinelgithub

1.1 sentinel同步nacos配置

  1. 增長sentinel的依賴和nacos存儲擴展依賴
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-datasource-nacos</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-web</artifactId>
</dependency>
  1. 添加nacos相關配置
spring:
  cloud:
    sentinel:
      datasource:
        # 名稱隨意
        javatrip:
          nacos:
            server-addr: 127.0.0.1:8848
            dataId: ${spring.application.name}-rules
            groupId: SENTINEL_GROUP
            # 規則類型,取值見:
            # org.springframework.cloud.alibaba.sentinel.datasource.RuleType
            rule-type: flow
  1. 提供接口用於測試限流
@RestController
class test{

    @RequestMapping("/test")
    public String test(){
        return "Java旅途";
    }
}
  1. nacos中增長限流規則的配置

  • resource:資源名,即限流規則的做用對象
  • limitApp:流控針對的調用來源,若爲 default 則不區分調用來源
  • grade:限流閾值類型(QPS 或併發線程數);0表明根據併發數量來限流,1表明根據QPS來進行流量控制
  • count:限流閾值
  • strategy:調用關係限流策略
  • controlBehavior:流量控制效果(直接拒絕、Warm Up、勻速排隊)
  • clusterMode:是否爲集羣模式
  1. 測試,訪問test接口,發現sentinel-dashboard中出現了一條流控規則

1.2 sentinel-dashboard中修改規則同步到nacos

要想實如今sentinel-dashboard中修改規則並同步到nacos,咱們就須要修改sentinel服務。首先咱們去官網下載Sentinel。web

  1. 修改pom文件
<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-datasource-nacos</artifactId>
    <scope>test</scope>
</dependency>

test 註釋掉,由於這個是做用與test目錄下的。redis

<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-datasource-nacos</artifactId>
    <!--<scope>test</scope>-->
</dependency>
  1. 找到 sentinel-dashboard/src/test/java/com/alibaba/csp/sentinel/dashboard/rule/nacos目錄,將整個目錄拷貝到 sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/rule/
  2. 找到 com.alibaba.csp.sentinel.dashboard.controller.v2.FlowControllerV2 ,將默認動態規則修改成nacos動態規則。
@Autowired
@Qualifier("flowRuleDefaultProvider")
private DynamicRuleProvider<List<FlowRuleEntity>> ruleProvider;
@Autowired
@Qualifier("flowRuleDefaultPublisher")
private DynamicRulePublisher<List<FlowRuleEntity>> rulePublisher;

修改成:spring

@Autowired
@Qualifier("flowRuleNacosProvider")
private DynamicRuleProvider<List<FlowRuleEntity>> ruleProvider;
@Autowired
@Qualifier("flowRuleNacosPublisher")
private DynamicRulePublisher<List<FlowRuleEntity>> rulePublisher;
  1. 找到 sentinel-dashboard/src/main/webapp/resources/app/scripts/directives/sidebar/sidebar.html

將如下內容註釋去掉json

<!--<li ui-sref-active="active" ng-if="entry.appType==0">-->
    <!--<a ui-sref="dashboard.flow({app: entry.app})">-->
    	<!--<i class="glyphicon glyphicon-filter"></i>&nbsp;&nbsp;流控規則 V1</a>-->
<!--</li>-->
  1. 從新編譯打包,運行打包後的sentinel-dashboard.jar。併發

  2. 測試,咱們刪除nacos中的流量規則配置app

  • 在sentinel-dashboard——>流量規則V1中新增一個規則。

  • 刷新nacos,發現多了一個配置

  • 在nacos中修改這個配置,將閥值改成1

  • 刷新sentinel-dashboard,流量閥值修改成1了。

  • 重啓服務,重啓sentinel-dashboard,發現流控規則依然存在。

注意:以上只是演示了流控規則的持久化,sentinel還支持其餘規則,若是想實現哪一種規則均可以採用相同的方式實現!

2、Gateway網關限流

限流:就是請求多了,對請求進行定製的快速響應處理,應用在服務提供者自己。

從 1.6.0 版本開始,Sentinel 提供了 Spring Cloud Gateway 的適配模塊,能夠提供兩種資源維度的限流:

  • route 維度:即在 Spring 配置文件中配置的路由條目,資源名爲對應的 routeId
  • 自定義 API 維度:用戶能夠利用 Sentinel 提供的 API 來自定義一些 API 分組
  1. 添加依賴
<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-spring-cloud-gateway-adapter</artifactId>
    <version>x.y.z</version>
</dependency>
<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-transport-simple-http</artifactId>
</dependency>
  1. 注入對應的 SentinelGatewayFilter 實例以及 SentinelGatewayBlockExceptionHandler 實例。
@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 MySentinelGatewayBlockExceptionHandler(viewResolvers,serverCodecConfigurer);
    }

    @Bean
    @Order(Ordered.HIGHEST_PRECEDENCE)
    public GlobalFilter sentinelGatewayFilter() {
        return new SentinelGatewayFilter();
    }
}
  1. 自定義異常處理
public class MySentinelGatewayBlockExceptionHandler extends SentinelGatewayBlockExceptionHandler {

    private List<ViewResolver> viewResolvers;
    private List<HttpMessageWriter<?>> messageWriters;
    public MySentinelGatewayBlockExceptionHandler(List<ViewResolver> viewResolvers, ServerCodecConfigurer serverCodecConfigurer) {
        super(viewResolvers,serverCodecConfigurer);
        this.viewResolvers = viewResolvers;
        this.messageWriters = serverCodecConfigurer.getWriters();
    }
    @Override
    public Mono<Void> handle(ServerWebExchange serverWebExchange, Throwable throwable) {

        if(serverWebExchange.getResponse().isCommitted()){
            return Mono.error(throwable);
        }
        if(!BlockException.isBlockException(throwable)){
            return Mono.error(throwable);
        }
        return handleBlockedRequest(serverWebExchange, throwable).flatMap(response -> writeResponse(response, serverWebExchange));
    }
    private Mono<ServerResponse> handleBlockedRequest(ServerWebExchange exchange, Throwable throwable) {
        return GatewayCallbackManager.getBlockHandler().handleRequest(exchange, throwable);
    }

    private final Supplier<ServerResponse.Context> contextSupplier = () -> new ServerResponse.Context() {
        @Override
        public List<HttpMessageWriter<?>> messageWriters() {
            return MySentinelGatewayBlockExceptionHandler.this.messageWriters;
        }

        @Override
        public List<ViewResolver> viewResolvers() {
            return MySentinelGatewayBlockExceptionHandler.this.viewResolvers;
        }
     };

    private Mono<Void> writeResponse(ServerResponse response, ServerWebExchange exchange) {
        ServerHttpResponse resp = exchange.getResponse();
        resp.getHeaders().add("Content-Type", "application/json;charset=UTF-8");
        String json = "{\"code\": -1, \"data\": null, \"msg\": \"訪問量過大,請稍後再試\"}";
        DataBuffer buffer = resp.bufferFactory().wrap(json.getBytes(StandardCharsets.UTF_8));
        return resp.writeWith(Mono.just(buffer));
    }
}
  1. 配置路由
server:
  port: 7003
spring:
  application:
    name: alibaba-gateway
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
    gateway:
      enabled: true
      discovery:
        locator:
          enabled: true # 開啓從註冊中心動態建立路由的功能,利用微服務名稱進行路由
      routes:
      - id: sentinel-nacos # 路由id,建議配合服務名
        uri: lb://sentinel-nacos #匹配路由名
        predicates:
          - Path=/sentinel/** # 斷言,路徑相匹配的進行路由
        filters:
          - StripPrefix=1
  1. 添加啓動參數
-Dcsp.sentinel.app.type=1 -Dcsp.sentinel.dashboard.server=localhost:8081 -Dproject.name=alibaba-gateway

  1. 訪問接口,查看效果

3、feign調用實現熔斷降級

降級:就是服務崩潰了,因此降級邏輯應該應用在消費者(調用者)那裏,加在服務提供者自己是毫無心義的,由於服務已經斷開了。

咱們根據實際需求在sentinel-dashboard中配置降級規則,而後編寫代碼。

  1. 定義接口
@RequestMapping("/test")
public String test(){
    return "Java旅途";
}
  1. 定義遠程服務調用接口
@FeignClient(name = "nacos-sentinel",fallback = RmoteTestFallback.class)
interface RemoteTest{
    @RequestMapping("/test")
    public String test();
}

爲了簡寫fallback,咱們更傾向於用fallbackFactory = RmoteTestFallbackFactory.class

@FeignClient(name = "nacos-sentinel",fallbackFactory = RmoteTestFallbackFactory.class)
interface RemoteTest{
    @RequestMapping("/test")
    public String test();
}
  1. 服務降級處理fallback
@Component
class RmoteTestFallback implements RemoteTest{
    @Override
    public String test() {
        return null;
    }
}
  1. 服務降級處理fallbackFactory
@Component
class RmoteTestFallbackFactory implements FallbackFactory<RemoteTest> {
    @Override
    public RemoteTest create(Throwable throwable) {
        return null;
    }
}
相關文章
相關標籤/搜索