一、sentinel的持久化配置
上一章中我们通过Dashboard来为Sentinel客户端设置各种各样的规则,但是这些规则默认是存放在内存中,极不稳定,无法用于生成环境,所以需要将其持久化。
DataSource
扩展常见的实现方式有:
- 拉模式:客户端主动向某个规则管理中心定期轮询拉取规则,这个规则中心可以是 RDBMS、文件,甚至是 VCS 等。这样做的方式是简单,缺点是无法及时获取变更;
- 推模式:规则中心统一推送,客户端通过注册监听器的方式时刻监听变化,比如使用 Nacos、Zookeeper 等配置中心。这种方式有更好的实时性和一致性保证。
Sentinel 目前支持以下数据源扩展:
- Pull-based: 文件、Consul
- Push-based: ZooKeeper, Redis, Nacos, Apollo, etcd
生产环境中一般常用的就是推模式
。这里我们使用Nacos存储规则。推送模式的正确做法应该是 配置中心控制台/Sentinel 控制台 → 配置中心 → Sentinel 数据源 → Sentinel。
1.1 sentinel同步nacos配置
- 增加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>
- 添加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
- 提供接口用于测试限流
@RestControllerclass test{ @RequestMapping("/test") public String test(){ return "Java旅途"; }}
- nacos中增加限流规则的配置
- resource:资源名,即限流规则的作用对象
- limitApp:流控针对的调用来源,若为 default 则不区分调用来源
- grade:限流阈值类型(QPS 或并发线程数);
0
代表根据并发数量来限流,1
代表根据QPS来进行流量控制 - count:限流阈值
- strategy:调用关系限流策略
- controlBehavior:流量控制效果(直接拒绝、Warm Up、匀速排队)
- clusterMode:是否为集群模式
- 测试,访问test接口,发现sentinel-dashboard中出现了一条流控规则
1.2 sentinel-dashboard中修改规则同步到nacos
要想实现在sentinel-dashboard中修改规则并同步到nacos,我们就需要修改sentinel服务。首先我们去官网下载Sentinel。
- 修改pom文件
<dependency> <groupId>com.alibaba.csp</groupId> <artifactId>sentinel-datasource-nacos</artifactId> <scope>test</scope></dependency>
将
<dependency> <groupId>com.alibaba.csp</groupId> <artifactId>sentinel-datasource-nacos</artifactId> <!--<scope>test</scope>--></dependency>
- 找到
sentinel-dashboard/src/test/java/com/alibaba/csp/sentinel/dashboard/rule/nacos
目录,将整个目录拷贝到sentinel-dashboard/src/main/java/com/alibaba/csp/sentinel/dashboard/rule/
。 - 找到
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;
修改为:
@Autowired@Qualifier("flowRuleNacosProvider")private DynamicRuleProvider<List<FlowRuleEntity>> ruleProvider;@Autowired@Qualifier("flowRuleNacosPublisher")private DynamicRulePublisher<List<FlowRuleEntity>> rulePublisher;
- 找到
sentinel-dashboard/src/main/webapp/resources/app/scripts/directives/sidebar/sidebar.html
将以下内容注释去掉
<!--<li ui-sref-active="active" ng-if="entry.appType==0">--> <!--<a ui-sref="dashboard.flow({app: entry.app})">--> <!--<i class="glyphicon glyphicon-filter"></i> 流控规则 V1</a>--><!--</li>-->
重新编译打包,运行打包后的sentinel-dashboard.jar。
测试,我们删除nacos中的流量规则配置
- 在sentinel-dashboard——>流量规则V1中新增一个规则。
- 刷新nacos,发现多了一个配置
- 在nacos中修改这个配置,将阀值改为1
- 刷新sentinel-dashboard,流量阀值修改为1了。
- 重启服务,重启sentinel-dashboard,发现流控规则依然存在。
注意:以上只是演示了流控规则的持久化,sentinel还支持其他规则,如果想实现哪种规则都可以采用相同的方式实现!
二、Gateway网关限流
限流:就是请求多了,对请求进行定制的快速响应处理,应用在服务提供者本身。
从 1.6.0 版本开始,Sentinel 提供了 Spring Cloud Gateway 的适配模块,可以提供两种资源维度的限流:
- route 维度:即在 Spring 配置文件中配置的路由条目,资源名为对应的 routeId
- 自定义 API 维度:用户可以利用 Sentinel 提供的 API 来自定义一些 API 分组
- 添加依赖
<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>
- 注入对应的
SentinelGatewayFilter
实例以及SentinelGatewayBlockExceptionHandler
实例。
@Configurationpublic 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(); }}
- 自定义异常处理
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)); }}
- 配置路由
server: port: 7003spring: 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
- 添加启动参数
-Dcsp.sentinel.app.type=1 -Dcsp.sentinel.dashboard.server=localhost:8081 -Dproject.name=alibaba-gateway
- 访问接口,查看效果
三、feign调用实现熔断降级
降级:就是服务崩溃了,所以降级逻辑应该应用在消费者(调用者)那里,加在服务提供者本身是毫无意义的,因为服务已经断开了。
我们根据实际需求在sentinel-dashboard中配置降级规则,然后编写代码。
- 定义接口
@RequestMapping("/test")public String test(){ return "Java旅途";}
- 定义远程服务调用接口
@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();}
- 服务降级处理fallback
@Componentclass RmoteTestFallback implements RemoteTest{ @Override public String test() { return null; }}
- 服务降级处理fallbackFactory
@Componentclass RmoteTestFallbackFactory implements FallbackFactory<RemoteTest> { @Override public RemoteTest create(Throwable throwable) { return null; }}
Spring Cloud Alibaba系列(六)sentinel的实际应用auction、 易速、 燕文物流、 亚马逊运营必学干货:3大数据分析方向、 敏思达、 巨大流量和爆款机会别流失!俚语词不容小觑!、 珠海一日游攻略大全、 珠海游8月份报价、 珠海游8月份报价、
No comments:
Post a Comment