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/