物联网架构成长之路(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/

posted @ 2020-03-20 07:42  无脑仔的小明  阅读(6782)  评论(0编辑  收藏  举报