• 物联网架构成长之路(55)-Gateway+Sentinel实现限流、熔断


    0. 前言
      之前有一篇博客讲到入门Sentinel,这次就将Sentinel引入到实际项目中进行演示。

    1.启动Sentinel
      具体可以参考这篇博客
      https://www.cnblogs.com/wunaozai/p/12404712.html

    java -jar sentinel-dashboadr-1.7.1.jar --server.port=8858

      项目中pom.xml引入

     1         <dependency>
     2             <groupId>com.alibaba.cloud</groupId>
     3             <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
     4         </dependency>
     5         <dependency>
     6             <groupId>com.alibaba.csp</groupId>
     7             <artifactId>sentinel-datasource-nacos</artifactId>
     8         </dependency>
     9         <dependency>
    10             <groupId>com.alibaba.csp</groupId>
    11             <artifactId>sentinel-transport-simple-http</artifactId>
    12         </dependency>
    13         <dependency>
    14             <groupId>com.alibaba.csp</groupId>
    15             <artifactId>sentinel-spring-cloud-gateway-adapter</artifactId>
    16         </dependency>

      以下配置追加到xxx-story-gateway-dev.properties的配置中,表示集成Sentinel

    1 spring.cloud.sentinel.transport.dashboard=127.0.0.1:8858
    2 spring.cloud.sentinel.datasource.ds.nacos.server-addr=127.0.0.1:8848
    3 spring.cloud.sentinel.datasource.ds.nacos.data-id=gateway-sentinel.properties
    4 spring.cloud.sentinel.datasource.ds.nacos.namespace=0e152861-1efa-62ea-9125-e569abc29691
    5 spring.cloud.sentinel.datasource.ds.nacos.group-id=DEFAULT_GROUP
    6 spring.cloud.sentinel.datasource.ds.nacos.data-type=json
    7 spring.cloud.sentinel.datasource.ds.nacos.rule-type=flow

    2. 动态修改Nacos上的Sentinel配置

     1 [
     2     {
     3         "resource": "/hello",
     4         "limitApp": "default",
     5         "grade": 1,
     6         "count": 5,
     7         "strategy": 0,
     8         "controlBehavior": 0,
     9         "clusterMode": false
    10     },
    11     {
    12         "resource": "/aiml/v1/ai/chat",
    13         "limitApp": "default",
    14         "grade": 1,
    15         "count": 2,
    16         "strategy": 0,
    17         "controlBehavior": 0,
    18         "clusterMode": false
    19     }
    20 ]

    3. 配置Sentinel Configuration
      在对流量进行限流和熔断过程中,需要一些自定义操作。此时就需要增加一个GatewayConfiguration配置类。因为默认提示异常是【Blocked by Sentinel: FlowException】,所以最好是能,自定义异常提示。
      GatewayConfiguration.java

     1 package com.wunaozai.demo.story.gateway.config;
     2 
     3 import java.util.Collections;
     4 import java.util.List;
     5 
     6 import org.springframework.beans.factory.ObjectProvider;
     7 import org.springframework.cloud.gateway.filter.GlobalFilter;
     8 import org.springframework.context.annotation.Bean;
     9 import org.springframework.context.annotation.Configuration;
    10 import org.springframework.core.Ordered;
    11 import org.springframework.core.annotation.Order;
    12 import org.springframework.http.codec.ServerCodecConfigurer;
    13 import org.springframework.web.reactive.result.view.ViewResolver;
    14 
    15 import com.alibaba.csp.sentinel.adapter.gateway.sc.SentinelGatewayFilter;
    16 
    17 @Configuration
    18 public class GatewayConfiguration {
    19 
    20     private final List<ViewResolver> views;
    21     private final ServerCodecConfigurer configurer;
    22     
    23     public GatewayConfiguration(ObjectProvider<List<ViewResolver>> views,
    24             ServerCodecConfigurer config) {
    25         this.views = views.getIfAvailable(Collections::emptyList);
    26         this.configurer = config;
    27     }
    28     
    29     /**
    30      * 配置SentinelGatewayBlockExceptionHandler,限流后异常处理
    31      * @return
    32      */
    33     @Bean
    34     @Order(Ordered.HIGHEST_PRECEDENCE)
    35     public JsonSentinelGatewayBlockExceptionHandler sentinelGatewayBlockExceptionHandler() {
    36         //return new SentinelGatewayBlockExceptionHandler(views, configurer);
    37         return new JsonSentinelGatewayBlockExceptionHandler(views, configurer);
    38     }
    39     
    40     /**
    41      * Sentinel 过滤器
    42      * @return
    43      */
    44     @Bean
    45     @Order(-1)
    46     public GlobalFilter sentinelGatewayFilter() {
    47         return new SentinelGatewayFilter();
    48     }
    49     
    50 }

      JsonSentinelGatewayBlockExceptionHandler.java

     1 package com.wunaozai.demo.story.gateway.config;
     2 
     3 import java.nio.charset.StandardCharsets;
     4 import java.util.List;
     5 
     6 import org.springframework.core.io.buffer.DataBuffer;
     7 import org.springframework.http.codec.HttpMessageWriter;
     8 import org.springframework.http.codec.ServerCodecConfigurer;
     9 import org.springframework.http.server.reactive.ServerHttpResponse;
    10 import org.springframework.web.reactive.function.server.ServerResponse;
    11 import org.springframework.web.reactive.result.view.ViewResolver;
    12 import org.springframework.web.server.ServerWebExchange;
    13 import org.springframework.web.server.WebExceptionHandler;
    14 
    15 import com.alibaba.csp.sentinel.adapter.gateway.sc.callback.GatewayCallbackManager;
    16 import com.alibaba.csp.sentinel.slots.block.BlockException;
    17 import com.alibaba.csp.sentinel.util.function.Supplier;
    18 
    19 import reactor.core.publisher.Mono;
    20 
    21 /**
    22  * Sentinel 限流后自定义异常
    23  * @author wunaozai
    24  * @Date 2020-03-17
    25  */
    26 public class JsonSentinelGatewayBlockExceptionHandler implements WebExceptionHandler {
    27 
    28     private List<ViewResolver> viewResolvers;
    29     private List<HttpMessageWriter<?>> messageWriters;
    30 
    31     public JsonSentinelGatewayBlockExceptionHandler(
    32             List<ViewResolver> viewResolvers, ServerCodecConfigurer serverCodecConfigurer) {
    33         this.viewResolvers = viewResolvers;
    34         this.messageWriters = serverCodecConfigurer.getWriters();
    35     }
    36     /**
    37      * 自定义返回
    38      * @param response
    39      * @param exchange
    40      * @return
    41      */
    42     private Mono<Void> writeResponse(ServerResponse response, ServerWebExchange exchange) {
    43         ServerHttpResponse resp = exchange.getResponse();
    44         resp.getHeaders().add("Content-Type", "application/json;charset=UTF-8");
    45         String json = "{"code": -1, "data": null, "msg": "系统限流"}";
    46         DataBuffer buffer = resp.bufferFactory().wrap(json.getBytes(StandardCharsets.UTF_8));
    47         return resp.writeWith(Mono.just(buffer));
    48     }
    49 
    50     @Override
    51     public Mono<Void> handle(ServerWebExchange exchange, Throwable ex) {
    52         if (exchange.getResponse().isCommitted()) {
    53             return Mono.error(ex);
    54         }
    55         if (!BlockException.isBlockException(ex)) {
    56             return Mono.error(ex);
    57         }
    58         return handleBlockedRequest(exchange, ex)
    59             .flatMap(response -> writeResponse(response, exchange));
    60     }
    61     private Mono<ServerResponse> handleBlockedRequest(ServerWebExchange exchange, Throwable throwable) {
    62         return GatewayCallbackManager.getBlockHandler().handleRequest(exchange, throwable);
    63     }
    64     private final Supplier<ServerResponse.Context> contextSupplier = () -> new ServerResponse.Context() {
    65         @Override
    66         public List<HttpMessageWriter<?>> messageWriters() {
    67             return JsonSentinelGatewayBlockExceptionHandler.this.messageWriters;
    68         }
    69         @Override
    70         public List<ViewResolver> viewResolvers() {
    71             return JsonSentinelGatewayBlockExceptionHandler.this.viewResolvers;
    72         }
    73     };
    74 }


    4. 熔断机制
      上面介绍的是限流,可以从 spring.cloud.sentinel.datasource.ds.nacos.rule-type=flow 看出来。但是根据 org.springframework.cloud.alibaba.sentinel.datasource.RuleType 这个枚举,Sentinel还提供了熔断机制。

      修改之前的配置,增加多一个数据源,一个是限流,一个是熔断

     1 #Sentinel
     2 spring.cloud.sentinel.transport.dashboard=127.0.0.1:8858
     3 spring.cloud.sentinel.datasource.ds1.nacos.server-addr=127.0.0.1:8848
     4 spring.cloud.sentinel.datasource.ds1.nacos.data-id=gateway-sentinel.properties
     5 spring.cloud.sentinel.datasource.ds1.nacos.namespace=0e152861-1efa-62ea-9125-e569abc29691
     6 spring.cloud.sentinel.datasource.ds1.nacos.group-id=DEFAULT_GROUP
     7 spring.cloud.sentinel.datasource.ds1.nacos.data-type=json
     8 spring.cloud.sentinel.datasource.ds1.nacos.rule-type=flow
     9 
    10 spring.cloud.sentinel.datasource.ds2.nacos.server-addr=127.0.0.1:8848
    11 spring.cloud.sentinel.datasource.ds2.nacos.data-id=gateway-sentinel-degrade.properties
    12 spring.cloud.sentinel.datasource.ds2.nacos.namespace=0e152861-1efa-62ea-9125-e569abc29691
    13 spring.cloud.sentinel.datasource.ds2.nacos.group-id=DEFAULT_GROUP
    14 spring.cloud.sentinel.datasource.ds2.nacos.data-type=json
    15 spring.cloud.sentinel.datasource.ds2.nacos.rule-type=degrade

      这个配置的意思是,如果请求xxx-story-res这个微服务是,当资源的平均响应时间超过阈值100ms后,资源进入准降级状态。如果接下来1秒内持续进入多个请求的RT时间都持续超过这个阈值,那么在接下来的时间窗口(timeWindow)30(秒)之内。对该方法的调用都会自动熔断。直接返回错误。这里会直接返回上面配置的 {"code": -1, "data": null, "msg": "系统限流"} 错误。

    1 [
    2   {
    3     "resource": "xxx-story-res",
    4     "count": 100,
    5     "grade": 0,
    6     "timeWindow": 30
    7   }
    8 ]

      现在测试,就是不断的请求对应的接口,从图中可以看到,每隔30秒,就会放一些请求到后面的服务。其他时间的请求,都是快速失败。返回限流。

    5. 附录各个在Nacos上的配置 

    参考资料:
      https://github.com/alibaba/Sentinel/wiki/%E7%83%AD%E7%82%B9%E5%8F%82%E6%95%B0%E9%99%90%E6%B5%81
      https://www.cnblogs.com/yinjihuan/p/10772558.html
      https://www.cnblogs.com/zhangpan1244/p/11228020.html

    本文地址:https://www.cnblogs.com/wunaozai/p/12512850.html
    本系列目录: https://www.cnblogs.com/wunaozai/p/8067577.html
    个人主页:https://www.wunaozai.com/

  • 相关阅读:
    Caused by: com.mysql.cj.core.exceptions.InvalidConnectionAttributeException: The server time zone value '�й���׼ʱ��' is unrecognized or represents more than one time zone. You must configure either the
    Caused by: java.lang.IllegalArgumentException: @EnableAsync annotation metadata was not injected
    jpa单向一对一关系外键映射
    jpa关联映射
    svn 插件去除已经保存的密码方法
    【前端】less学习
    【CodeForces 520E】Pluses everywhere
    费马小定理证明
    【前端】纯前端的一个‘喜欢我吗?’
    Sublime text3 插件HTML/CSS/JS prettify 格式化代码
  • 原文地址:https://www.cnblogs.com/wunaozai/p/12512850.html
Copyright © 2020-2023  润新知